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: () { ... },
)
Related
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.
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(),
)
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--;
});
}
},
basically I have a swiping screen with elements, where user is able to swipe in left or right direction. When the user is swiping, im calling some functions. Im using GestureDetector for gesture recognitions and PageView.Custom for my items. Probably ListView.Custom does also work, but it doesn't fix my issue I have.
I need a PageController, because I have to control the navigation programatically. And I think the PageController maybe is the reason behind my issue that my functions are called multiple times. How to fix it? Does somebody know why setstate is called that often and what to do to prevent it?
Im providing you a fully working example (minified version) with a print on the swiping right actions, where you can see that its beeing called multiple times.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
// Page selector for tab list
void _selectPage(int index) {
print('page index: $index');
setState(() {
currentIndex = index;
});
}
// Routes list for tab navigation Android
final List<Widget> _pages = [
ScreenA(),
ScreenB(func: _selectPage),
];
return Scaffold(
appBar: AppBar(),
body: _pages[currentIndex],
bottomNavigationBar: SafeArea(
child: BottomNavigationBar(
onTap: _selectPage,
iconSize: 22,
currentIndex: currentIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.description),
label: 'ScreenA',
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.ac_unit_outlined),
label: 'ScreenB'),
],
),
),
);
}
}
class ScreenA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text('HOME'),
);
}
}
class ScreenB extends StatefulWidget {
Function func;
ScreenB({Key key, #required this.func})
: super(key: key);
#override
_ScreenBState createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
_ScreenBState();
var _controller = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.access_alarm_sharp),
onPressed: () async {
widget.func(0);
},
),
],
),
body: PageView.custom(
dragStartBehavior: DragStartBehavior.start,
controller: _controller,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
childrenDelegate: SliverChildBuilderDelegate((ctx, pageIndex) =>
GestureDetector(
onPanUpdate: (details) async {
if (details.delta.dx < 0) {
_controller.nextPage(
duration: Duration(milliseconds: 200),
curve: Curves.easeInOut);
print('function called');
}
},
child: Center(
child: Container(
width: 200,
height: 200,
color: Colors.red,
child: Text('hi')))))),
);
}
}
Thanks in advance!
The problem is that you are using the onPanUpdate method, which is triggered every time a user drags their finger either right or left. You should use the onPanEnd method, which is only triggered when the user's finger is off the screen after dragging either left or right. The function below will work fine.
onPanEnd: (details) async { if (details.velocity.pixelsPerSecond.dx < 0) { _controller.nextPage( duration: Duration(milliseconds: 200), curve: Curves.easeInOut); print('function called'); } }
Please write this function out of widget build function
// Page selector for tab list
void _selectPage(int index) {
print('page index: $index');
setState(() {
currentIndex = index;
});
}
like this
import 'package:flutter/gestures.dart';
import 'package:flutter/material.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = 0;
// Page selector for tab list
void _selectPage(int index) {
print('page index: $index');
setState(() {
currentIndex = index;
});
}
// Routes list for tab navigation Android
final List<Widget> _pages = [
ScreenA(),
ScreenB(func: _selectPage),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: _pages[currentIndex],
bottomNavigationBar: SafeArea(
child: BottomNavigationBar(
onTap: _selectPage,
iconSize: 22,
currentIndex: currentIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.description),
label: 'ScreenA',
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.ac_unit_outlined),
label: 'ScreenB'),
],
),
),
);
}
}
class ScreenA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text('HOME'),
);
}
}
class ScreenB extends StatefulWidget {
Function func;
ScreenB({Key key, #required this.func})
: super(key: key);
#override
_ScreenBState createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
_ScreenBState();
var _controller = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.access_alarm_sharp),
onPressed: () async {
widget.func(0);
},
),
],
),
body: PageView.custom(
dragStartBehavior: DragStartBehavior.start,
controller: _controller,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
childrenDelegate: SliverChildBuilderDelegate((ctx, pageIndex) =>
GestureDetector(
onPanUpdate: (details) async {
if (details.delta.dx < 0) {
_controller.nextPage(
duration: Duration(milliseconds: 200),
curve: Curves.easeInOut);
print('function called');
}
},
child: Center(
child: Container(
width: 200,
height: 200,
color: Colors.red,
child: Text('hi')))))),
);
}
}
I want to remember the item that was clicked in drawer .
I am using the same widget for drawer ( sameDrawerOnly ) in all three widgets ( MyHomePage , FirstPage and SecondPage) and using variable itemClicked to trackthe item that was tapped inside setState . But the conditional formatting is not working.
Here is the code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
DrawerOnly sameDrawerOnly = DrawerOnly();
class MyApp extends StatelessWidget {
final appTitle = 'Drawer Demo';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('My Page!')),
drawer: sameDrawerOnly,
);
}
}
class DrawerOnly extends StatefulWidget {
const DrawerOnly ({
Key key,
}) : super(key: key);
#override
_DrawerOnlyState createState() => _DrawerOnlyState();
}
class _DrawerOnlyState extends State<DrawerOnly > {
int itemClicked = 0;
#override
Widget build(BuildContext ctxt) {
return Drawer(
child: new ListView(
children: <Widget>[
new DrawerHeader(
child: new Text("DRAWER HEADER.."),
decoration: new BoxDecoration(
color: Colors.orange
),
),
new ListTile(
title: new Text("Item => A", style: itemClicked==1 ? TextStyle( fontWeight: FontWeight.bold, color: Colors.red.withOpacity(0.6) ) : null),
onTap: () {
Navigator.pop(ctxt);
setState(() {
itemClicked=1;
});
Navigator.push(ctxt,
new MaterialPageRoute(builder: (ctxt) => new FirstPage()));
},
),
new ListTile(
title: new Text("Item => 2", style: itemClicked==2 ? TextStyle( fontWeight: FontWeight.bold , color: Colors.green.withOpacity(0.6) ) : TextStyle()),
onTap: () {
Navigator.pop(ctxt);
setState(() {
itemClicked=2;
});
Navigator.push(ctxt,
new MaterialPageRoute(builder: (ctxt) => new SecondPage()));
},
),
],
)
);
}
}
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
drawer: sameDrawerOnly,
appBar: new AppBar(title: new Text("First Page"),),
body: new Text("I belongs to First Page"),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
drawer: sameDrawerOnly,
appBar: new AppBar(title: new Text("Second Page"),),
body: new Text("I belongs to Second Page"),
);
}
}
What went wrong
Although sameDrawerOnly was declared at the top most part of your file. Everytime the widget re-draws your app's screens, eg. opening FirstPage via MaterialPageRoute, the variable in the DrawerOnly widget will always stay to zero. Because it is always re-drawn based on your configuration.
What you can do
Hotfix: Make itemClicked a static variable. (Not Recommended)
// Before
int itemClicked
// After
static int itemClicked
Alternatively, you can refactor your code and use PageView instead of opening a new Scaffold widget every time you switch between drawer items. Then, you can now use currentPageValue to determine what item was selected by the user.
MyHomePage.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final appTitle = 'Drawer Demo';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
PageController _pageController;
double currentPageValue = 0.0;
#override
void initState() {
super.initState();
_pageController = PageController();
_pageController.addListener(() {
setState(() {
currentPageValue = _pageController.page;
// Do whatever you like with the page value
});
});
}
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: PageView(
controller: _pageController,
children: <Widget>[
FirstPage(),
SecondPage(),
],
),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
_pageController.jumpToPage(0);
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
_pageController.jumpToPage(1);
Navigator.pop(context);
},
),
],
),
),
);
}
}
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(color: Colors.red);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(color: Colors.yellow);
}
}
View on dartpad.dev.
More on:
https://flutter.dev/docs/cookbook/design/drawer