Freely moveable Flutter Widget - flutter

I need a special Widget.
Hey, I need the name pros.
Is there a widget that can be moved freely. Like how you can just move on with maps?
So basically scrollable in all directions.

You can check InteractiveViewer
Her is a basic demo:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late Offset _offset;
#override
void initState() {
_offset = const Offset(0, 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_offset.toString())),
body: SizedBox.expand(
child: InteractiveViewer(
onInteractionUpdate: (details) => setState(() {
_offset = details.focalPoint;
}),
boundaryMargin: const EdgeInsets.all(1000.0),
minScale: 0.1,
maxScale: 3,
child: Center(
child: TextButton(
child: const Text('Drag me'),
onPressed: () {
},
),
),
),
),
);
}
}

You can use draggable widget for that simply wrap your widget like this
Draggable(
data: 'Flutter',
child: FlutterLogo(
size: 100.0,
),
feedback: FlutterLogo(
size: 100.0,
),
childWhenDragging: Container(),
)

Related

CustomPainter's paint method is not getting called before WidgetsBinding.instance.addPostFrameCallback in case of Multiple navigation

I have a Flutter StatefulWidget and in initState() method I am using WidgetsBinding.instance.addPostFrameCallback to use one instance variable (late List _tracks). like -
WidgetsBinding.instance.addPostFrameCallback((_) {
for(itr = 0; itr<_tracks.length; itr++){
// some logic
}
});
As this would get invoked after all Widgets are done. In one of the CustomPaint's painter class I am initializing that variable.
SizedBox.expand(
child: CustomPaint(
painter: TrackPainter(
trackCalculationListener: (tracks) {
_tracks = tracks;
}),
),
),
It is working fine when I have one screen, i.e the same class. But, When I am adding one screen before that and trying to navigate to this screen from the new screen it is throwing _tracks is not initialized exception.
new screen is very basic -
class MainMenu extends StatefulWidget {
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Play(),
maintainState: false));
},
child: const Text('play game'),
),
),
);
}
}
In single screen case the paint method of painter is getting called before postFrameCallback but in case of multiple it is not getting before postFrameCallback and because of that the variable is not getting initialized.
reproducible code -
import 'package:flutter/material.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(
primarySwatch: Colors.blue,
),
routes: {
'/mainMenu': (context) => const MainMenu(),
'/game': (context) => const MyHomePage(title: 'game'),
},
initialRoute: '/mainMenu',
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<Rect> _playerTracks;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print(_playerTracks.length);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
color: Colors.white,
margin: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: 1,
child: SizedBox.expand(
child: CustomPaint(
painter: RectanglePainter(
trackCalculationListener: (playerTracks) =>
_playerTracks = playerTracks),
),
),
),
)
],
),
),
);
}
}
class MainMenu extends StatefulWidget {
static String route = '/mainMenu';
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 200.0,
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/game');
},
child: const Text('play game'),
),
),
),
);
}
}
class RectanglePainter extends CustomPainter {
Function(List<Rect>) trackCalculationListener;
RectanglePainter({required this.trackCalculationListener});
#override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const RadialGradient gradient = RadialGradient(
center: Alignment(0.7, -0.6),
radius: 0.2,
colors: <Color>[Color(0xFFFFFF00), Color(0xFF0099FF)],
stops: <double>[0.4, 1.0],
);
canvas.drawRect(
rect,
Paint()..shader = gradient.createShader(rect),
);
List<Rect> _playerTracks = [];
_playerTracks.add(rect);
trackCalculationListener(_playerTracks);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
I am very new to flutter and would highly appreciate if someone could help me figure out what I am doing wrong here.

Setstate not working when adding a widget to the background? [SOLVED]

The widget I made for the background color is causing a problem.
There is a problem when a child widget is added to the Background Color widget I made. In this case setstate doesn't work.
Setstate not working when adding a widget to the background?
Why is the screen not updating?
Why do you think this is not happening? Where am I doing wrong?
//zemin_rengi.dart
import 'package:flutter/material.dart';
class ZeminRengi extends StatefulWidget {
final Widget childWidget;
const ZeminRengi({required this.childWidget});
#override
State<ZeminRengi> createState() => _ZeminRengiState();
}
class _ZeminRengiState extends State<ZeminRengi> {
Widget? _childWidget;
#override
void initState() {
// TODO: implement initState
super.initState();
_childWidget = widget.childWidget;
}
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.blue, Colors.red]),
),
child: _childWidget,
);
}
}
//main.dart
import 'package:builk/zemin_rengi.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ZeminRengi(
childWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
thanks for help.
enter image description here

AnimatedSize not tweening unless if there's parent widget

I observe there is no tweening unless if an AnimatedSize widget has a parent Container. In the below code, the square goes to size zero if you tap on the square. If I remove the AnimatedSize widget's parent, the widget immediately goes to size zero without tweening. Furthermore, there is no tweening if I keep the parent Container but remove the color field or make it transparent.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
double _size = 200.0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() {
_size = 0;
}),
child: Container( // No tweening if this widget is removed
color: Colors.red, // No tweening if this field is removed or made transparent
child: AnimatedSize(
curve: Curves.easeIn,
duration: const Duration(seconds: 1),
child: Container(
width: _size,
height: _size,
color: Colors.red,
)),
),
);
}
}
Why is the parent widget needed? Ideally I would like the tweening to happen without the need of this parent.
With TweenAnimationBuilder I didn't need the outer widget::
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
double _endSize = 200.0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() {
_endSize = 0;
}),
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 200.0, end: _endSize),
curve: Curves.easeIn,
duration: const Duration(seconds: 1),
builder: (context, size, child) {
return Container(
width: size,
height: size,
color: Colors.red,
);
}),
);
}
}

Flutter - Animate a widget to move from GridView to BottomBar upon tapping

I am looking to animate an image widget to move from a grid view to the bottom bar as shown below but much simpler. Could anyone provide me any guidance as to how to achieve this? I am leaning towards a transform animation, but I have hit a wall trying to calculate the source and destination screen points. Any help is highly appreciated.
Try this package, 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() {
// TODO: implement 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,
),
);
}
}

Flutter how to prevent scroll in a specific area in PageView

I have a page with tabbars as header and Pageview for body. The problem that I'm facing is due to the PageView is scrollable and one of the pages requires to do signatures, when I drag to draw on the signature widget, it makes the whole PageView to scroll. Is there a way to stop pageview to scroll while drawing signatures? Like stop gesture from passing to parent widget?
Thanks
My simple sample code:
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
bottom: ColoredTabBar(
tabBarBackgroundColor,
TabBar(
isScrollable: true,
controller: _tabController,
tabs: _tabsInfo.map((EditSafetyPlanTab tabInfo) {
return Tab(
text: tabInfo.label,
);
}).toList()),
),
),
body: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
if (isPageCanChanged) {
onPageChange(index);
}
},
itemCount: _tabsInfo.length,
itemBuilder: (context, index) => buildPage(index, _tabsInfo),
),
);
Update
I had to add "MyHorizontalDragGestureRecognizer" and enable/disable scroll physics to make it work on Android.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class AppScrollBehavior extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
scrollBehavior: AppScrollBehavior(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class MyHorizontalDragGestureRecognizer
extends HorizontalDragGestureRecognizer {
#override
void rejectGesture(int pointer) {
acceptGesture(pointer);
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Offset offset = Offset.zero;
final PageController controller = PageController();
ScrollPhysics physics = AlwaysScrollableScrollPhysics();
#override
Widget build(BuildContext context) {
return PageView(
physics: physics,
controller: controller,
children: <Widget>[
Center(
child: RawGestureDetector(
gestures: {
MyHorizontalDragGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
MyHorizontalDragGestureRecognizer>(
() => MyHorizontalDragGestureRecognizer(),
(instance) {
instance.onDown = (_) => disableScroll();
instance.onCancel = () => enableScroll();
instance.onEnd = (_) => enableScroll();
instance.onUpdate = (details) {
setState(() {
offset = details.localPosition;
});
};
},
),
},
child: Container(
color: const Color(0xFFCCCCCC),
width: 200,
height: 200,
child: Center(
child: Text(
'x: ${offset.dx.toStringAsFixed(0)}, y: ${offset.dy.toStringAsFixed(0)}',
),
),
),
),
),
const Center(
child: Text('Second Page'),
),
],
);
}
disableScroll() {
setState(() {
physics = NeverScrollableScrollPhysics();
});
}
enableScroll() {
setState(() {
physics = AlwaysScrollableScrollPhysics();
});
}
}
You have to wrap your widget with RawGestureDetector and register a HorizontalDragGestureRecognizer or a VerticalDragGestureRecognizer depending on your scrollDirection.
The GestureRecognizer of the signature widget will win against the recognizer of the PageView in the gesture arena.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class AppScrollBehavior extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
scrollBehavior: AppScrollBehavior(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Offset offset = Offset.zero;
final PageController controller = PageController();
#override
Widget build(BuildContext context) {
return PageView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller,
children: <Widget>[
Center(
child: RawGestureDetector(
gestures: {
HorizontalDragGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(),
(instance) {
instance.onUpdate = (details) {
setState(() {
offset = details.localPosition;
});
};
},
),
},
child: Container(
color: const Color(0xFFCCCCCC),
width: 200,
height: 200,
child: Center(
child: Text(
'x: ${offset.dx.toStringAsFixed(0)}, y: ${offset.dy.toStringAsFixed(0)}',
),
),
),
),
),
const Center(
child: Text('Second Page'),
),
],
);
}
}
You can use behavior property of GestureDetector:
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () { ... },
)