How to disable scrolling simulation? - flutter

I have implemented a scrollable container with a horizontal direction. The scrolling simulation is applied when I changed the content dimension of the scrollable container after reached it to the maxScrollExtend position. Please anyone let me know that how to restrict it while updating the scrolling content dimension. I need to restrict it for my use case.
Code snippet:
class NestedDemo extends StatefulWidget {
NestedDemo({Key? key}) : super(key: key);
#override
_NestedDemoState createState() => _NestedDemoState();
}
class _NestedDemoState extends State<NestedDemo> {
double width = 500;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
color: Colors.lightBlue[100],
height: 350,
width: width,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
width = 250.0;
setState(() {});
},
),
);
}
}

Not sure if you want to reach maxScrollExtend position on triggering an event. If you are trying to reach maxScrollExtend position on event. You can try the below snippet
import 'package:flutter/material.dart';
import 'dart:async';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: NestedDemo(),
),
),
);
}
}
class NestedDemo extends StatefulWidget {
NestedDemo({Key? key}) : super(key: key);
#override
_NestedDemoState createState() => _NestedDemoState();
}
class _NestedDemoState extends State<NestedDemo> {
double width = 5000;
ScrollController _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: Container(
color: Colors.lightBlue[100],
height: 350,
width: width,
),
),
TextButton(child: Text('Please here'), onPressed:(){
Timer(
Duration(milliseconds: 300),
() => _scrollController.jumpTo(
_scrollController.position.maxScrollExtent));
}),
]),
floatingActionButton: FloatingActionButton(
onPressed: () {
width = 250.0;
setState(() {});
},
),
);
}
}
If you want to stop the scrolling physics: NeverScrollableScrollPhysics()

Related

Freely moveable Flutter Widget

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(),
)

How to navigate pageview pages with overwrite back button in AppBar?

I have a pageview view and it works with sliding. But how do I integrate this back button as leading: Icon(backbutton), when navigating between forms in the pageview? Thanks
screen1.dart
import 'package:app/src/features/examples/components/body.dart';
class OnboardingExampleFlowPage extends StatelessWidget {
static String routeName = "/onboarding_example_flow";
const OnboardingExampleFlowPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
extendBodyBehindAppBar: true,
appBar: AppBar(
elevation: 1,
backgroundColor: AppColors.monochromeWhite,
title: Text(context.l10n.buttonBack),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {},
),
),
body: const Body(),
);
}
}
Body has pageview:
body.dart
class _BodyState extends State<Body> {
int currentPage = 0;
final PageController controller = PageController();
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
final List<Widget> formPages = <Widget>[
ExampleContent01(controller: controller),
ExampleContent02(controller: controller),
ExampleContent03(controller: controller),
ExampleContent04(controller: controller),
];
return SafeArea(
child: SizedBox(
child: Column(
children: [
const SizedBox(height: 6),
AppStepper(
currentPage: currentPage,
length: formPages.length,
noSkip: true,
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(20),
),
child: PageView(
controller: controller,
onPageChanged: (value) => setState(() => currentPage = value),
children: formPages,
),
),
),
],
),
),
);
}
These forms: There are contents in ExampleScreens, but I did not add their code because there are AppBar and Pageview in the code I added.
here is view: want to be able to go back inside pageview.
Thanks a lot!
Just move the controller up, to the parent widget, so it's possible to navigate the pages with it.
Check out the live demo on DartPad.
The code is going to be like the following:
import 'package:flutter/gestures.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 OnboardingExampleFlowPage(),
scrollBehavior: MyCustomScrollBehavior(),
debugShowCheckedModeBanner: false,
);
}
}
class OnboardingExampleFlowPage extends StatefulWidget {
static String routeName = "/onboarding_example_flow";
const OnboardingExampleFlowPage({Key? key}) : super(key: key);
#override
State<OnboardingExampleFlowPage> createState() =>
_OnboardingExampleFlowPageState();
}
class _OnboardingExampleFlowPageState extends State<OnboardingExampleFlowPage> {
final PageController controller = PageController();
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
extendBodyBehindAppBar: true,
appBar: AppBar(
elevation: 1,
title: const Text('Back'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
controller.previousPage(
duration: const Duration(milliseconds: 250),
curve: Curves.easeOut,
);
},
),
),
body: Body(controller: controller),
);
}
}
class Body extends StatefulWidget {
const Body({super.key, required this.controller});
final PageController controller;
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
int currentPage = 0;
#override
Widget build(BuildContext context) {
const List<Widget> formPages = [
Center(child: Text('Page 1')),
Center(child: Text('Page 2')),
Center(child: Text('Page 3')),
Center(child: Text('Page 4')),
];
return SafeArea(
child: SizedBox(
child: Column(
children: [
const SizedBox(height: 6),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: PageView(
controller: widget.controller,
onPageChanged: (value) => setState(() => currentPage = value),
children: formPages,
),
),
),
],
),
),
);
}
}
// Enables scrolling with mouse dragging
class MyCustomScrollBehavior extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
Dont have body widget in separate file
Put it in the _OnboardingExampleFlowPageState instead.
And it is the _OnboardingExampleFlowPageState that should have controller and
currentIndex variables.
So on leading button click you'll do something like this:
onPressed: () {
if (currentPage > 0) {
controller.previousPage(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
);
setState(() {
currentPage--;
});
}
},

Flutter scrollbar that is in horizontal scrollview doesnt show correctly inner scrollview

So, I have bigger scroll view that scrolls horizontally, and inside - little box (red color) and smaller scrollview (orange color) that scrolls vertically.
There are two scrollbars on the bigger scrollview (1 - for horizontal), and second - for vertical inner.
And the problem - vertical scrollbar doesnt look right, because it can go only like blue arrow shows, and I want it to have either full height of the bigger scrollview, or be right near scrollable vertical part, but then dont hide itself if scrolled in horizontal direction.
Run on dartPad
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ScrollSizingWidget(),
),
),
);
}
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({
Key? key,
}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
final ScrollController _horizontal = ScrollController();
final ScrollController _vertical = ScrollController();
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scrollbar(
controller: _vertical,
notificationPredicate: (notification) => notification.depth == 1,
child: Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children:[
Container(width: 1000, height: 200, color: Colors.green),
Flexible(
child: SingleChildScrollView(
controller: _vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.yellow,
)
)
),
]
)
),
),
),
);
}
}
I have used your code to reproduce the issue. If I understood your needs right, here is the fix:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Container(
height: 1000,
width: 1000,
color: Colors.orange,
),
],
),
),
),
),
),
);
}
Firstly, you say that your main SingleChildScrollView scrolls horizontally, but your widget tree starts with a Scrollbar which uses a vertical ScrollController. So you should create your widgets step by step, as you explained.
Also, since you want to see the vertical Scrollbar through your main SingleChildScrollView, I wrapped both(red and orange Containers) with Scrollbar and SingleChildScrollView to have the effect you want. Furthermore, I connected these Scrollbar and SingleChildScrollView with the same horizontal ScrollController. So now, not only the orange Container, but both are scrollable and stick together, not independent.
If you don't want the red Container being scrolled along with the orange Container, check this:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Expanded(
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.orange,
),
),
),
),
],
),
),
),
);
}
Lastly, Scrollbar's position in iOS is a bit buggy because of the notch, etc. So I wrapped your ScrollSizingWidget with SafeArea to fix the issue in iOS.
If these answers are not what you expect, please don't hesitate to write.
Edit: After your explanations in the comments below, I have created another fix. I believe CustomScrollView and Sliver widgets are fits here perfectly. The red Container, which you want to stay in its position, should be wrapped with the SliverAppBar. Lastly, the orange Container, which you want to be able to scroll vertically, could be wrapped with SliverFixedExtentList. Please check the code below:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: CustomScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
slivers: [
SliverAppBar(
toolbarHeight: 200.0,
collapsedHeight: 200.0,
pinned: true,
stretch: true,
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Container(
height: 200.0,
color: Colors.red,
),
titleSpacing: 0,
),
SliverFixedExtentList(
itemExtent: 1200.0,
delegate: SliverChildBuilderDelegate(
(_, __) => Container(
color: Colors.orange,
),
childCount: 1,
),
),
],
),
),
),
),
);
}

Flutter hero animation between widgets not screens

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

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: () { ... },
)