I'm trying to create draggable and resizable widgets (Matrix4 Gesture Detector
Widget) wrapped inside a Stack
widget. 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,
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,
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"),
)
],
),
),
);
}
}
DraggableText
class,
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: