Unable to change the width after "Transform.scale"?
In the following sample code, the red Container, do not change width after setting fitWidth(width of container) when pressing the floatingbutton.
Only if it is les than MediaQuery.of(context).size.width.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
// State
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Matrix4 matrix = Matrix4.identity();
double fitWidth;
#override
void initState() {
fitWidth = 0;
super.initState();
}
double getSizeWidth(BuildContext context) {
if (fitWidth != 0) {
return fitWidth;
} else {
return MediaQuery.of(context).size.width;
}
}
Widget build(BuildContext context) {
var sizWidth = getSizeWidth(context);
print(sizWidth);
return Scaffold(
appBar: AppBar(
title: const Text('Sample Code'),
),
body: Transform.scale(
scale: 0.5,
child: Container(
color: Colors.red,
width: sizWidth,
height: 400,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
fitWidth = 800;
});
// Add your onPressed code here!
},
backgroundColor: Colors.green,
));
}
}
its works for me
Widget build(BuildContext context) {
print(sizWidth);
return Scaffold(
appBar: AppBar(
title: const Text('Sample Code'),
),
body: Transform.scale(
scale: 0.5,
child: Container(
color: Colors.red,
width: fitWidth != 0?fitWidth:MediaQuery.of(context).size.width,
height: 400,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
fitWidth = 800;
});
// Add your onPressed code here!
},
backgroundColor: Colors.green,
));
}
cheers...
try this,
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Matrix4 matrix = Matrix4.identity();
double widthScale = 0.0;
#override
void initState() {
super.initState();
}
double getSizeWidth(BuildContext context) {
if (widthScale != 0.0) {
return widthScale;
} else {
return 1.0;
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample Code'),
),
body: Transform(
alignment: Alignment.center,
transform: new Matrix4.identity()..scale(getSizeWidth(context), 0.5),
child: Container(
color: Colors.red,
width: MediaQuery.of(context).size.width,
height: 400,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widthScale = 0.5;
});
// Add your onPressed code here!
},
backgroundColor: Colors.green,
));
}
}
Give alignment(Align)
Transform.scale(
scale: 0.5,
child: Center( // or Align
child: Container(
color: Colors.red,
width: sizWidth,
height: 400,
),
),
)
Related
Onclick of a Filter icon in the appbar, I should display a Filter screen of 1/3 width on top of the current screen of full width.. May I know how to do this in Flutter?
Three possibilities:
Use a Drawer
https://flutter.dev/docs/cookbook/design/drawer
This is designed to do exactly what you want. Works with Scaffolds
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool showOverlay = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("app bar")),
drawer: Drawer(),
body: Center(
child: Text("Test"),
),
);
}
}
Overlay Entry
https://api.flutter.dev/flutter/widgets/OverlayEntry-class.html
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
OverlayEntry? overlay;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("app bar")),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
final _overlay = _createOverlayEntry(context);
Overlay.of(context)!.insert(_overlay);
setState(() {
overlay = _overlay;
});
},
),
body: Center(
child: Text("Test"),
));
}
OverlayEntry _createOverlayEntry(
BuildContext context,
) {
return OverlayEntry(
builder: (context) => Stack(
children: [
Positioned(
width: MediaQuery.of(context).size.width * (1.0 / 3.0),
height: MediaQuery.of(context).size.height,
top: 0,
left: 0,
child: Container(
color: Colors.red,
child: Center(
child: ElevatedButton(
child: Text("close"),
onPressed: () {
// close overlay
overlay?.remove();
},
),
),
),
),
],
),
);
}
}
Simple Stack
https://api.flutter.dev/flutter/widgets/Stack-class.html
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool showOverlay = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Scaffold(
appBar: AppBar(title: Text("app bar")),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
showOverlay = true;
});
},
),
body: Center(
child: Text("Test"),
),
),
if (showOverlay)
Positioned(
width: MediaQuery.of(context).size.width * (1.0 / 3.0),
height: MediaQuery.of(context).size.height,
top: 0,
left: 0,
child: Container(
color: Colors.red,
child: Center(
child: ElevatedButton(
child: Text("close"),
onPressed: () {
setState(() {
showOverlay = false;
});
},
),
),
),
),
],
);
}
}
I am having a problem where when I try to use a switch widget it will not work properly inside of an alert box as in it does not switch over to the second state it just bounces whenever I try to flick it. I am wondering if this is because there is a problem with the switch itself or how I displayed it in the box? Thanks!
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
home: SwitchDemo(),
);
}
}
class SwitchDemo extends StatefulWidget {
const SwitchDemo({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<SwitchDemo> {
bool isInstructionView;
#override
void initState() {
super.initState();
isInstructionView = Global.shared.isInstructionView;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("add data"),
),
body: Container(
child: TextButton(
child: Text('Open Alert Box'),
onPressed: () => {
showDialog(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 20,
vertical:
MediaQuery.of(context).size.height / 20,
),
child: AlertDialog(
content: Container(
child: Switch(
value: isInstructionView,
onChanged: (bool isOn) {
if (isInstructionView == false) {
} else if (isInstructionView == true) {}
setState(() {
isInstructionView = isOn;
Global.shared.isInstructionView = isOn;
isOn = !isOn;
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
),
);
})
}),
));
}
}
class Global {
static final shared = Global();
bool isInstructionView = false;
}
Wrap you AlertDialog with StatefulBuilder.
here is full code:
import 'package:flutter/material.dart';
class SwitchDemo extends StatefulWidget {
const SwitchDemo({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<SwitchDemo> {
late bool isInstructionView;
#override
void initState() {
super.initState();
isInstructionView = Global.shared.isInstructionView;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("add data"),
),
body: Container(
child: TextButton(
child: Text('Open Alert Box'),
onPressed: () => {
showDialog(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 20,
vertical: MediaQuery.of(context).size.height / 20,
),
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
content: Container(
child: Switch(
value: isInstructionView,
onChanged: (bool isOn) {
print(isInstructionView);
setState(() {
isInstructionView = !isInstructionView;
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
);
}),
);
},
)
}),
));
}
}
class Global {
static final shared = Global();
bool isInstructionView = false;
}
Does it answer your question?
ref: https://stackoverflow.com/a/57240941/10157127
Hero animation is the best for navigating between screen, but I need same animation between widgets. Like one card moving another place for example: Product Card moves to shoppingcart and something else. Thanks for answers!
Try this one, add_to_cart_animation:
import 'package:add_to_cart_animation/add_to_cart_animation.dart';
import 'package:add_to_cart_animation/add_to_cart_icon.dart';
import 'package:flutter/material.dart';
import 'list_item.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Add To Cart Animation',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Add To Cart Animation'),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// We can detech the location of the card by this GlobalKey<CartIconKey>
GlobalKey<CartIconKey> gkCart = GlobalKey<CartIconKey>();
late Function(GlobalKey) runAddToCardAnimation;
var _cartQuantityItems = 0;
#override
Widget build(BuildContext context) {
return AddToCartAnimation(
// To send the library the location of the Cart icon
gkCart: gkCart,
rotation: true,
dragToCardCurve: Curves.easeIn,
dragToCardDuration: const Duration(milliseconds: 1000),
previewCurve: Curves.linearToEaseOut,
previewDuration: const Duration(milliseconds: 500),
previewHeight: 30,
previewWidth: 30,
opacity: 0.85,
initiaJump: false,
receiveCreateAddToCardAnimationMethod: (addToCardAnimationMethod) {
// You can run the animation by addToCardAnimationMethod, just pass trough the the global key of the image as parameter
this.runAddToCardAnimation = addToCardAnimationMethod;
},
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: false,
actions: [
// Improvement/Suggestion 4.4 -> Adding 'clear-cart-button'
IconButton(
icon: Icon(Icons.cleaning_services),
onPressed: () {
_cartQuantityItems = 0;
gkCart.currentState!.runClearCartAnimation();
},
),
SizedBox(width: 16),
AddToCartIcon(
key: gkCart,
icon: Icon(Icons.shopping_cart),
colorBadge: Colors.red,
),
SizedBox(
width: 16,
)
],
),
body: ListView(
children: [
AppListItem(onClick: listClick, index: 1),
AppListItem(onClick: listClick, index: 2),
AppListItem(onClick: listClick, index: 3),
AppListItem(onClick: listClick, index: 4),
AppListItem(onClick: listClick, index: 5),
AppListItem(onClick: listClick, index: 6),
AppListItem(onClick: listClick, index: 7),
],
),
),
);
}
// Improvement/Suggestion 4.4 -> Running AddTOCartAnimation BEFORE runCArtAnimation
void listClick(GlobalKey gkImageContainer) async {
await runAddToCardAnimation(gkImageContainer);
await gkCart.currentState!.runCartAnimation((++_cartQuantityItems).toString());
}
}
OR
[not null safety]
this is a sample of add to cart, add_cart_parabola:
import 'dart:ui';
import 'package:add_cart_parabola/add_cart_parabola.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
GlobalKey floatKey = GlobalKey();
GlobalKey rootKey = GlobalKey();
Offset floatOffset ;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
RenderBox renderBox = floatKey.currentContext.findRenderObject();
floatOffset = renderBox.localToGlobal(Offset.zero);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
key: rootKey,
width: double.infinity,
height: double.infinity,
color: Colors.grey,
child: ListView(
children: List.generate(40, (index){
return generateItem(index);
}).toList(),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.yellow,
key: floatKey,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget generateItem(int index){
Text text = Text("item $index",style: TextStyle(fontSize:
25),);
Offset temp;
return GestureDetector(
onPanDown: (details){
temp = new Offset(details.globalPosition.dx, details.globalPosition
.dy);
},
onTap: (){
Function callback ;
setState(() {
OverlayEntry entry = OverlayEntry(
builder: (ctx){
return ParabolaAnimateWidget(rootKey,temp,floatOffset,
Icon(Icons.cancel,color: Colors.greenAccent,),callback,);
}
);
callback = (status){
if(status == AnimationStatus.completed){
entry?.remove();
}
};
Overlay.of(rootKey.currentContext).insert(entry);
});
},
child: Container(
color: Colors.orange,
child: text,
),
);
}
}
For animating widget in the same screen you can use AnimatedPositioned widget see the below code
import 'dart:math';
import 'package:flutter/material.dart';
class AnimatedPositionedDemo extends StatefulWidget {
const AnimatedPositionedDemo({Key? key}) : super(key: key);
static String routeName = 'animated_positioned';
#override
_AnimatedPositionedDemoState createState() => _AnimatedPositionedDemoState();
}
class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
late double topPosition;
late double leftPosition;
double generateTopPosition(double top) => Random().nextDouble() * top;
double generateLeftPosition(double left) => Random().nextDouble() * left;
#override
void initState() {
super.initState();
topPosition = generateTopPosition(30);
leftPosition = generateLeftPosition(30);
}
void changePosition(double top, double left) {
setState(() {
topPosition = generateTopPosition(top);
leftPosition = generateLeftPosition(left);
});
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final appBar = AppBar(title: const Text('AnimatedPositioned'));
final topPadding = MediaQuery.of(context).padding.top;
// AnimatedPositioned animates changes to a widget's position within a Stack
return Scaffold(
appBar: appBar,
body: SizedBox(
height: size.height,
width: size.width,
child: Stack(
children: [
AnimatedPositioned(
top: topPosition,
left: leftPosition,
duration: const Duration(seconds: 1),
child: InkWell(
onTap: () => changePosition(
size.height -
(appBar.preferredSize.height + topPadding + 50),
size.width - 150),
child: Container(
alignment: Alignment.center,
width: 150,
height: 50,
child: Text(
'Click Me',
style: TextStyle(
color:
Theme.of(context).buttonTheme.colorScheme!.onPrimary,
),
),
color: Theme.of(context).primaryColor,
),
),
),
],
),
),
);
}
}
I hope it works for you
For Animated widgets, flutter team has provided a video on youtube here
And you can read all about them on their website here
Link to code.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Moving Box')),
body: Stack(
children: <Widget>[
Align(
alignment: FractionalOffset(1, 0),
child: Container(
// Use the properties stored in the State class.
width: _width,
height: _height,
color: Color(0xffe5ee22),
),
),
],
)
// body: _buildBody(context)
);
}
According to your gist, you are planning to use AnimatedContainer
so, I tried to fixed the code for you with minimal modification as possible
Must Wrap AnimatedContainerApp into MaterialApp Widget
Change this code :
void main() => runApp(AnimatedContainerApp());
class AnimatedContainerApp extends StatefulWidget {
#override
_AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}
into this :
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: AnimatedContainerApp(),
);
}
}
class AnimatedContainerApp extends StatefulWidget {
#override
_AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}
Change Container to Animated Container
Change this part :
child: Container(
// Use the properties stored in the State class.
width: _width,
height: _height,
color: Color(0xffe5ee22),
),
into this :
child: AnimatedContainer( // Changed to AnimatedContainer
duration: Duration(seconds: 1), // Duration is mandatory
width: _width,
height: _height,
color: Color(0xffe5ee22),
),
Create button which triggers animation by calling setState()
class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
bool isSmall = true;
double _width = 50;
double _height = 50;
void changeSize() {
// Business Logic
if (isSmall) {
setState(() {
_width = 100;
_height = 100;
isSmall = false;
});
} else {
setState(() {
_width = 50;
_height = 50;
isSmall = true;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Moving Box'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add_to_home_screen),
onPressed: changeSize, // this will toggle size of container
)
],
),
Demo
Full Code :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: AnimatedContainerApp(),
);
}
}
class AnimatedContainerApp extends StatefulWidget {
#override
_AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}
class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
bool isSmall = true;
double _width = 50;
double _height = 50;
void changeSize() {
// Toggle State
if (isSmall) {
setState(() {
_width = 100;
_height = 100;
isSmall = false;
});
} else {
setState(() {
_width = 50;
_height = 50;
isSmall = true;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Moving Box'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add_to_home_screen),
onPressed: changeSize,
)
],
),
body: Stack(
children: <Widget>[
Align(
alignment: FractionalOffset(1, 0),
child: AnimatedContainer( // Changed to AnimatedContainer
duration: Duration(seconds: 1), // Add Duration
width: _width,
height: _height,
color: Color(0xffe5ee22),
),
),
],
),
);
}
}
I am using flutter and i have a container with the shape of a circle using this code
new Container(
width: 50.0,
height: 50.0,
decoration: new BoxDecoration(
shape: BoxShape.circle)
I want to make this circle move on the screen like this
how can I do this?
Here it is:
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Drag app"),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
double width = 100.0, height = 100.0;
Offset position ;
#override
void initState() {
super.initState();
position = Offset(0.0, height - 20);
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
left: position.dx,
//top: position.dy - height + 20,
child: Draggable(
child: Container(
width: width,
height: height,
color: Colors.blue,
child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
),
feedback: Container(
child: Center(
child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
color: Colors.red[800],
width: width,
height: height,
),
onDraggableCanceled: (Velocity velocity, Offset offset){
setState(() => position = offset);
},
),
),
],
);
}
}
What you are looking for is Draggable widget. You can then handle the translation using onDraggableCanceled which is passed and offset that you can be used to update the placement
onDraggableCanceled :(velocity,offset){
//update the position here
}
Update
After checking the image you will need "Drop me here" part to be a DragTarget that has a method onAccept which will handles the logic when you drag and drop your Draggable
First, wrap your Container inside the Stack with Positioned.
Then, use Pan Gesture to implement a Pan in your Container and use onPan... methods to handle Pan Gesture
Here is code:
Offset position;
#override
void initState() {
super.initState();
position = Offset(10, 10);
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
double _height = _width * 9 / 16;
return GestureDetector(
onPanStart: (details) => _onPanStart(context, details),
onPanUpdate: (details) => _onPanUpdate(context, details, position),
onPanEnd: (details) => _onPanEnd(context, details),
onPanCancel: () => _onPanCancel(context),
child: SafeArea(
child: Stack(
children: <Widget>[
Positioned(
top: position.dy,
child: Container(
color: Colors.red,
width: _width,
height: _height,
),
),
],
),
),
);
}
void _onPanStart(BuildContext context, DragStartDetails details) {
print(details.globalPosition.dy);
}
void _onPanUpdate(BuildContext context, DragUpdateDetails details, Offset offset) {
setState(() {
position = details.globalPosition;
});
}
void _onPanEnd(BuildContext context, DragEndDetails details) {
print(details.velocity);
}
void _onPanCancel(BuildContext context) {
print("Pan canceled !!");
}
Hope this helps!
You can use Draggable class for dragging the item which you want to drag and for placing it or sticking it to somewhere on the screen you have to wrap that item with DragTarget class. In DragTarget class onAccept method is there where you can write the logic. You can also take a reference to my code here it is
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.indigo,
),
home: new MyHomePage(title: 'Flutter Demo Drag Box'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body:
new DragGame(), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class DragGame extends StatefulWidget {
#override
_DragGameState createState() => new _DragGameState();
}
class _DragGameState extends State<DragGame> {
int boxNumberIsDragged;
#override
void initState() {
boxNumberIsDragged = null;
super.initState();
}
#override
Widget build(BuildContext context) {
return new Container(
constraints: BoxConstraints.expand(),
color: Colors.grey,
child: new Stack(
children: <Widget>[
buildDraggableBox(1, Colors.red, new Offset(30.0, 100.0)),
buildDraggableBox(2, Colors.yellow, new Offset(30.0, 200.0)),
buildDraggableBox(3, Colors.green, new Offset(30.0, 300.0)),
],
));
}
Widget buildDraggableBox(int boxNumber, Color color, Offset offset) {
return new Draggable(
maxSimultaneousDrags: boxNumberIsDragged == null || boxNumber == boxNumberIsDragged ? 1 : 0,
child: _buildBox(color, offset),
feedback: _buildBox(color, offset),
childWhenDragging: _buildBox(color, offset, onlyBorder: true),
onDragStarted: () {
setState((){
boxNumberIsDragged = boxNumber;
});
},
onDragCompleted: () {
setState((){
boxNumberIsDragged = null;
});
},
onDraggableCanceled: (_,__) {
setState((){
boxNumberIsDragged = null;
});
},
);
}
Widget _buildBox(Color color, Offset offset, {bool onlyBorder: false}) {
return new Container(
height: 50.0,
width: 50.0,
margin: EdgeInsets.only(left: offset.dx, top: offset.dy),
decoration: BoxDecoration(
color: !onlyBorder ? color : Colors.grey,
border: Border.all(color: color)),
);
}
}