Home > Net >  How to prevent draggable widgets (created with Matrix4 Transform) in a Stack from changing their pos
How to prevent draggable widgets (created with Matrix4 Transform) in a Stack from changing their pos

Time:11-05

I'm trying to create draggable and resizable widgets (Matrix4 Gesture Detector Widget) wrapped inside a Stackwidget. I'm able to drag and resize the widgets in the way I want them to, but the problem is, when I programatically add another widget to the Stack's children, all the draggable widgets which were previously positioned revert and comes back to the center along with the newly added dragabble widget.

Let's say there are 2 draggable widgets in the Stack initially and we also positioned them,

enter image description here

When I add another widget programatically by tapping on the Add button, the positions are lost and every draggable widget appears to the center like this,

enter image description here

How can I fix this issue and prevent the widgets from not reverting to the center? Here is the code.

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: DummyProvider())
      ],
    child: MaterialApp(
      home: MyApp(),
    ),
    )
  );
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    DummyProvider dummyProvider =
        Provider.of<DummyProvider>(context, listen: true);
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            Container(
              height: Constants.deviceHeight! * .6,
              color: Colors.red,
              child: Stack(
                children: List.generate(
                    dummyProvider.list.length,
                    (index) => Drag(
                          text: Text(dummyProvider.list[index].text,
                              style: TextStyle(fontSize: 40)),
                        )),
              ),
            ),
            ElevatedButton(
              onPressed: () {
                dummyProvider.addToList(DraggableText(
                    id: DateTime.now().millisecondsSinceEpoch.toString(),
                    text: String.fromCharCodes(List.generate(
                        5, (index) => Random().nextInt(33)   89))));
              },
              child: Text("Add"),
            )
          ],
        ),
      ),
    );
  }
}

DraggableTextclass,

class DraggableText {
  String id = '';
  String text = '';
  FontWeight fontWeight = FontWeight.normal;
  FontStyle fontStyle = FontStyle.normal;
  TextDecoration fontDecoration = TextDecoration.none;
  Matrix4 position = Matrix4.identity();
  DraggableText({required this.id, required this.text});
}

The provider that has all the draggable widget,

class DummyProvider extends ChangeNotifier {
  List<DraggableText> list = [];
  String currentText = '';

  void addToList(DraggableText text) {
    list.add(text);
    notifyListeners();
  }
}

The dragabble text widget using Matrix4 Gesture Detector Package,

class Drag extends StatelessWidget {
  final Text text;
  const Drag({required this.text});

  @override
  Widget build(BuildContext context) {
    final ValueNotifier<Matrix4> notifier = ValueNotifier(Matrix4.identity());
    return MatrixGestureDetector(
      onMatrixUpdate: (m, tm, sm, rm) {
        notifier.value = m;
        print("$m $tm $sm $rm");
      },
      child: AnimatedBuilder(
        animation: notifier,
        builder: (ctx, child) {
          return Transform(
            transform: notifier.value,
            child: Center(
  

        child: Stack(
            children: <Widget>[
              Transform.scale(
                scale: 1,
                origin: Offset(0.0, 0.0),
                child: GestureDetector(
                    child:
                        Container(color: Colors.blueAccent, child: text)),
              ),
            ],
          ),
        ),
      );
    },
  ),
);}}

CodePudding user response:

The issue with generator, it is refreshing the list including the positions, every setState it is creating newList. I added extra property to model class to handle it and some extra-methods at notifier while testing the issue, this may help in the future.


void main() {
  runApp(MultiProvider(
    providers: [ChangeNotifierProvider.value(value: DummyProvider())],
    child: const MaterialApp(
      home: MyApp(),
    ),
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) => SafeArea(
          child: Column(
            children: [
              Container(
                height: constraints.maxHeight * .6,
                color: Colors.red,
                child: Consumer<DummyProvider>(
                  builder: (context, value, child) => Stack(
                    children: value.list
                        .map(
                          (d) => Drag(
                            key: ValueKey(d.id),
                            notifier: d.notifier!,
                            callBack: (
                              Matrix4 matrix,
                            ) {
                              // print("${matrix.row0.a}  ${matrix.row1.a}");
                              print(matrix);
                            },
                            text: Text(
                              d.text,
                              style: TextStyle(fontSize: 40),
                            ),
                          ),
                        )
                        .toList(),
                  ),
                ),
              ),
              ElevatedButton(
                onPressed: () {
                  final provider = context.read<DummyProvider>();
                  provider.addToList(
                    DraggableText(
                      id: DateTime.now().millisecondsSinceEpoch.toString(),
                      text: String.fromCharCodes(
                        List.generate(5, (index) => Random().nextInt(33)   89),
                      ),
                    ),
                  );
                },
                child: Text("Add"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class Drag extends StatelessWidget {
  final Text text;
  final ValueNotifier<Matrix4> notifier;
  final Function callBack;

  const Drag({
    Key? key,
    required this.text,
    required this.callBack,
    required this.notifier,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MatrixGestureDetector(
      onMatrixUpdate: (m, tm, sm, rm) {
        notifier.value = m;
        // print("$m $tm $sm $rm");
        callBack(notifier.value);
      },
      child: AnimatedBuilder(
        animation: notifier,
        builder: (ctx, child) {
          print("${notifier.value.row0.a}  ${notifier.value.row1.a}");

          return Transform(
            transform: notifier.value,
            child: Center(
              child: Stack(
                children: <Widget>[
                  Transform.scale(
                    scale: 1,
                    origin: Offset(0.0, 0.0),
                    child: GestureDetector(
                      child: Container(
                        color: Colors.blueAccent,
                        child: text,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

class DraggableText {
  String id = '';
  String text = '';
  FontWeight fontWeight = FontWeight.normal;
  FontStyle fontStyle = FontStyle.normal;
  TextDecoration fontDecoration = TextDecoration.none;
  Matrix4 position = Matrix4.identity();
  ValueNotifier<Matrix4>? notifier;
  DraggableText({
    required this.id,
    required this.text,
  }) {
    this.notifier = this.notifier ?? ValueNotifier(Matrix4.identity());
  }
}

class DummyProvider extends ChangeNotifier {
  List<DraggableText> list = [];
  String currentText = '';

  void addToList(DraggableText text) {
    list.add(text);
    notifyListeners();
  }

  void updatePosition(int index, Matrix4 position) {
    list[index].position = position;
    notifyListeners();
  }

  void updatePositionByItem(DraggableText text, Matrix4 position) {
    list.firstWhere((element) => element == text).position = position;
    notifyListeners();
  }
}

CodePudding user response:

try this in stateless:

class Drag extends StatelessWidget {
  final Text text;

  const Drag({Key? key, required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {

or in stateful:

class Drag extends StatefulWidget {
  final Text text;

  Drag({
    Key? key,
    required this.text,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => DragState();
}

class DragState extends State<Drag> {

use it with:

Drag(
  key: UniqueKey(),
  text: 
  • Related