interactive_box 0.2.0 copy "interactive_box: ^0.2.0" to clipboard
interactive_box: ^0.2.0 copied to clipboard

outdated

A Flutter widget that has pre-defined design for scaling, rotating, moving interaction.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:interactive_box/interactive_box.dart';
import 'package:uuid/uuid.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: "title"),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double xPosition = 100.0; // default should be 0
  double yPosition = 100.0; // default should be 0
  double width = 150;
  double height = 150;
  double maxWidth = 200;
  double maxHeight = 200;
  double rotate = 0;

  // Just a sample, you may use your own List.
  List<TableModel> tables = [];

  late TransformationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TransformationController();
  }

  @override
  Widget build(BuildContext context) {
    final Size screenSize = MediaQuery.of(context).size;

    return Container(
      padding: const EdgeInsets.all(18),
      child: SingleChildScrollView(
        child: Column(
          children: [
            Align(
              alignment: Alignment.centerRight,
              child: Padding(
                padding: const EdgeInsets.all(18),
                child: ElevatedButton(
                  onPressed: () {
                    setState(() {
                      tables.add(
                        TableModel(
                          id: const Uuid().v4(),
                          showIcons: false,
                          width: 300,
                          height: 300,
                          x: 0,
                          y: 0,
                          rotateAngle: 0,
                          image: Image.asset(
                            "assets/table.png",
                            fit: BoxFit.fill,
                          ),
                        ),
                      );
                    });
                  },
                  child: const Text("Import"),
                ),
              ),
            ),
            ConstrainedBox(
              constraints: BoxConstraints.loose(
                Size(
                  screenSize.width,
                  screenSize.height,
                ),
              ),

              ///
              /// Create infinite screen.
              /// ref: https://stackoverflow.com/a/70915030
              ///
              /// Author: @Tor-Martin Holen
              ///
              child: InteractiveViewer.builder(
                transformationController: _controller,
                builder: (context, quad) {
                  return Center(
                    child: SizedBox(
                      width: screenSize.width,
                      height: screenSize.height,
                      child: GridPaper(
                        child: Stack(
                          clipBehavior: Clip.none,
                          fit: StackFit.passthrough,
                          children: [
                            ...List.generate(tables.length, (index) {
                              TableModel table = tables[index];

                              return InteractiveBox(
                                key: ObjectKey(table),
                                initialWidth: table.width,
                                initialHeight: table.height,
                                initialShowActionIcons: table.showIcons,
                                initialX: table.x,
                                initialY: table.y,
                                initialRotateAngle: table.rotateAngle,
                                circularMenuDegree: 180,
                                iconSize: 40,
                                onInteractiveActionPerformed: (_, boxInfo) {
                                  /// Copy boxInfo and update to table after performing interactive actions
                                  /// so that when we toggle the icons in [onTap] method below, all tables
                                  /// will be updated correctly (persist their position, size, rotate angle)...
                                  ///
                                  ///
                                  /// Without this you might see the other tables not updated correctly.
                                  /// To demo this problem,
                                  /// 1. Comment the code below
                                  /// 2. Press "import" button twice
                                  /// 3. Drag the second image to another side
                                  /// 4. Tap the first image
                                  ///
                                  /// and you will see the second image is jumped to its original position.
                                  ///

                                  // START comment here if want to see the problem
                                  TableModel copiedTable = table.copyWith(
                                    width: boxInfo.width,
                                    height: boxInfo.height,
                                    rotateAngle: boxInfo.rotateAngle,
                                    x: boxInfo.x,
                                    y: boxInfo.y,
                                  );

                                  int idx = tables.indexOf(table);

                                  setState(() {
                                    tables[idx] = copiedTable;
                                  });

                                  // END comment here if want to see the problem
                                },
                                onTap: (boxInfo) {
                                  /// Only tapped table will show icons.
                                  ///
                                  /// Might refer to [onInteractiveActionPerformed]
                                  ///
                                  setState(() {
                                    tables = tables
                                        .map((e) => e.id == table.id
                                            ? e.copyWith(showIcons: true)
                                            : e.copyWith(showIcons: false))
                                        .toList();
                                  });
                                },
                                onActionSelected: (actionType, boxInfo) {
                                  if (actionType == ControlActionType.copy) {
                                    TableModel copiedTable = table.copyWith(
                                      id: const Uuid().v4(),
                                      width: boxInfo.width,
                                      height: boxInfo.height,
                                      rotateAngle: boxInfo.rotateAngle,
                                      x: boxInfo.x + 50,
                                      y: boxInfo.y + 50,
                                    );

                                    setState(() {
                                      tables.add(copiedTable);
                                    });
                                  } else if (actionType ==
                                      ControlActionType.delete) {
                                    int idx = tables.indexOf(table);
                                    setState(() {
                                      tables.removeAt(idx);
                                    });
                                  }
                                },
                                includedActions: const [
                                  ControlActionType.copy,
                                  ControlActionType.delete,
                                  ControlActionType.move,
                                  ControlActionType.rotate,
                                  ControlActionType.scale,
                                ],
                                child: tables[index].image,
                              );
                            })
                          ],
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

@immutable
class TableModel {
  final String id;
  final double width;
  final double height;
  final double x;
  final double y;
  final double rotateAngle;
  final bool showIcons;
  final Widget image;

  const TableModel({
    required this.width,
    required this.height,
    required this.x,
    required this.y,
    required this.rotateAngle,
    required this.image,
    required this.id,
    required this.showIcons,
  });

  TableModel copyWith({
    String? id,
    double? width,
    double? height,
    double? x,
    double? y,
    double? rotateAngle,
    Widget? image,
    bool? showIcons,
  }) {
    return TableModel(
      id: id ?? this.id,
      showIcons: showIcons ?? this.showIcons,
      width: width ?? this.width,
      height: height ?? this.height,
      x: x ?? this.x,
      y: y ?? this.y,
      rotateAngle: rotateAngle ?? this.rotateAngle,
      image: image ?? this.image,
    );
  }
}
21
likes
0
points
93
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter widget that has pre-defined design for scaling, rotating, moving interaction.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on interactive_box