How to implement a right navbar in flutter? - flutter

The flutter scaffold shows a right navbar, but I suppose there is no right nav widget. How do I implement a right navbar with scaffold in flutter?
Flutter Scaffold Image

The Scaffold now has a endDrawer property which swipes from right-to-left.
Hope this might help someone.

If you are trying to show a right bar/menu or Drawer in your app, whether it is has a permanent view or a temporary one. I was able to achieve this by building my own custom widget from Allign, Container and Column widgets, and by using setState to show or hide the menu bar based on user interaction, see this simple example.
My custom menu widget looks like the following:
class RightNavigationBar extends StatefulWidget {
#override
_RightNavigationBarState createState() => new _RightNavigationBarState();
}
class _RightNavigationBarState extends State<RightNavigationBar> {
#override
Widget build(BuildContext context) {
return new Align(
alignment: FractionalOffset.centerRight,
child: new Container(
child: new Column(
children: <Widget>[
new Icon(Icons.navigate_next),
new Icon(Icons.close),
new Text ("More items..")
],
),
color: Colors.blueGrey,
height: 700.0,
width: 200.0,
),
);
}
}
Then when the user presses the menu icon, an object of my custom RightNavigationBar widget is created inside setState :
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _myRightBar = null;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
actions: [new IconButton(
icon: new Icon (Icons.menu), onPressed: _showRightBar)
],
title: new Text("Right Navigation Bar Example"),
),
body: _myRightBar
);
}
_showRightBar() {
setState(() {
_myRightBar == null
? _myRightBar = new RightNavigationBar()
: _myRightBar = null;
});
}
}

vertical_navigation_bar
How to use it? #
Install
dependencies:
vertical_navigation_bar: ^0.0.1
Run flutter command
flutter pub get
import 'package:flutter/material.dart';
import 'package:vertical_navigation_bar/vertical_navigation_bar.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Abubakr Elghazawy (Software Developer)',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final pageController = PageController(
initialPage: 0,
keepPage: true
);
final navItems = [
SideNavigationItem(icon: FontAwesomeIcons.calendarCheck, title: "New task"),
SideNavigationItem(icon: FontAwesomeIcons.calendarAlt, title: "Personal task"),
SideNavigationItem(icon: FontAwesomeIcons.fileAlt, title: "Personal document"),
SideNavigationItem(icon: FontAwesomeIcons.calendar, title: "Company task"),
SideNavigationItem(icon: FontAwesomeIcons.arrowCircleRight, title: "Options")
];
final initialTab = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
SideNavigation(
navItems: this.navItems,
itemSelected: (index){
pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.linear
);
},
initialIndex: 0,
actions: <Widget>[
],
),
Expanded(
child: PageView.builder(
itemCount: 5,
controller: pageController,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index){
return Container(
color: Colors.blueGrey.withOpacity(0.1),
child: Center(
child: Text("Page " + index.toString()),
)
);
},
),
)
],
),
);
}
}
More Dtailsenter link description here

Use the endDrawer property of the Scaffold like this:
Scaffold(
resizeToAvoidBottomInset: false,
key: _scaffoldKey,
endDrawer: const SideBar(),
body: CustomScrollView(

Related

How to manipulate the ScrollController in Flutter's sliding up panel plugin?

I'm using Flutter's sliding_up_panel plugin.
I want to scroll the panel to the top when a new item is selected from my app drawer. Presently selecting a new item closes the panel and refreshes the panel content. Then opens it to a 200px peak, but it doesn't reset the panel's Scroll location to the top.
I've been going around in circles trying the same solutions in slightly different ways and getting nowhere.
What I've tried:
I have global
PanelController slidingPanelController = new PanelController();
ScrollController slideUpPanelScrollController = new ScrollController();
I tried attaching my global slideUpPanelScrollController to my panel's listview, but when swiping up the panel's ListView it simultaneously starts closing the whole panel. If you were scrolling up to read the content you'd skimmed, well, you're not able to because it's disappearing.
Preventing this bug is easy, you do it the canonical way from the plugin's examples, pass the ScrollController through from SlidingPanel and therefore create a local ScrollController in the panel's Listview.
panelBuilder: (slideUpPanelScrollController) => _scrollingList(presentLocation, slideUpPanelScrollController)
The problem then is, you can't scroll the panel on new App drawer selections, because the controller is now local.
I tried putting a listener on the local listview ScrollController, _slideUpPanelScrollController and testing for panelController.close():
if(slidingPanelController.isPanelClosed) _slideUpPanelScrollController.jumpTo(0);
But the listener blocked the panel from swiping, swipe events fired but the panel didn't swipe, or was extremely reluctant too.
Having freshly selected content open in the panel displaying content halfway down the ListView is a glitchy user experience. I would love some ideas or better solutions.
I need it so when the panel is closed, I can
slideUpPanelScrollController.jumpTo(0);
I need the global controller to attach to the panel ListView's local controller, or I need a way to access the local controller to fire its Scroll from outwith my _scrollingList() function.
Here's the panel Widget:
SlidingUpPanel(
key: Key("slidingUpPanelKey"),
borderRadius: slidingPanelBorderRadius,
parallaxEnabled: false,
controller: slidingPanelController,
isDraggable: isDraggableBool,
onPanelOpened: () {
},
onPanelSlide: (value) {
if (value >= 0.98)
setState(() {
slidingPanelBorderRadius =
BorderRadius.vertical(top: Radius.circular(16));
});
},
onPanelClosed: () async {
setState(() {
listViewScrollingOff = true;
});
imageZoomController.value =
Matrix4.identity(); // so next Panel doesn't have zoomed in image
slidingPanelBorderRadius =
BorderRadius.vertical(top: Radius.circular(16));
},
minHeight: panelMinHeight,
maxHeight:
MediaQuery.of(context).size.height - AppBar().preferredSize.height,
panelBuilder: (slideUpPanelScrollController) => _scrollingList(presentLocation, slideUpPanelScrollController),
body: ...
Here's the _scrollingList Widget:
Widget _scrollingList(LocationDetails presentLocation, ScrollController _slideUpPanelScrollController ) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView(
controller: _slideUpPanelScrollController,
physics: listViewScrollingOff
? const NeverScrollableScrollPhysics()
: const AlwaysScrollableScrollPhysics(),
key: Key("scrollingPanelListView"),
children: [
This is my onTap from my Drawer ListView item:
onTap: () {
if(slidingPanelController.isPanelShown) {
//slideUpPanelScrollController.jumpTo(0);
slidingPanelController.close();
}
Love help! Below is I think a minimum viable problem. I wrote it in Dartpad, but sharing from Dartpad is nontrivial, so I've copied and pasted it here. Dartpad doesn't support the plugin anyway so it's not like you could tweak it there.
import 'package:flutter/material.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
PanelController slidingPanelController = new PanelController();
ScrollController slideUpPanelScrollController = new ScrollController();
final String title = "sliding panel";
String panelContent = "";
String stupidText = "";
String stupidText2 = ""
int panelMinHeight = 0;
int teaserPanelHeight = 77;
bool listViewScrollingOff = false;
initState() {
super.initState();
for(int i = 0; i < 500; i++) {
stupidText += "More stupid text. ";
}
for(int i = 0; i < 500; i++) {
stupidText2 += "More dumb, dumbest text. ";
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: Text(title)),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Item 1'),
onTap: () {
if(slidingPanelController.isPanelShown) {
print('attempting to scroll to top and close panel');
//slideUpPanelScrollController.jumpTo(0);
slidingPanelController.close();
}
Navigator.of(context).pop();
setState() {
panelContent = stupidText1;
panelMinHeight = teaserPanelHeight;
}
},
),
ListTile(
title: const Text('Item 2'),
onTap: () {
if(slidingPanelController.isPanelShown) {
//slideUpPanelScrollController.jumpTo(0);
slidingPanelController.close();
}
Navigator.of(context).pop();
setState() {
panelContent = stupidText2;
panelMinHeight = teaserPanelHeight;
}
}
),
],
),
),
body: SlidingUpPanel(
key: Key("slidingUpPanelKey"),
borderRadius: 8,
parallaxEnabled: false,
controller: slidingPanelController,
isDraggable: true,
onPanelOpened: () async {
setState(() {
listViewScrollingOff = false;
panelMinHeight = 0;
animatedMarkerMap;
//slideUpPanelScrollController.jumpTo(0);
});
},
onPanelSlide: (value) {
print("onPanelSlide: attempting to scroll panel");
},
onPanelClosed: () async {
setState(() {
//slideUpPanelScrollController.jumpTo(0);
listViewScrollingOff = true;
});
},
minHeight: panelMinHeight,
maxHeight:
MediaQuery.of(context).size.height - AppBar().preferredSize.height,
// TODO BUG
// SAM, IF I USE PANELBUILDER's ScrollController attached to the panel's ListView, then, when closing, the ListView will move to the top first, then the panel closes,
// however ListView's controller is set to a globalController, this causes a bug when closing the panel, but means you can open/peek the panel from the App drawer,
panelBuilder: (slideUpPanelScrollController) => _scrollingList(panelContent, slideUpPanelScrollController),
body: Center(
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
),
),
),
),
);
}
Widget _scrollingList(String panelContent, ScrollController _slideUpPanelScrollController ) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView(
controller: _slideUpPanelScrollController,
physics: listViewScrollingOff
? const NeverScrollableScrollPhysics()
: const AlwaysScrollableScrollPhysics(),
key: Key("scrollingPanelListView"),
children: [Text(panelContent)])));
}
}
OK, so the problem became that when I closed the sliding panel 'naturally', by scrolling back up the panel to it top then sliding the panel down, well both things happened at once.
I've found how to solve this, I need to set SlidingUpPanel's isDraggable property to false, till the user has scrolled to the top of the panel.
Like so...
#override
void initState() {
super.initState();
slideUpPanelScrollController.addListener(() {
if(slideUpPanelScrollController.offset == 0) {
setState(() {
isDraggableBool = true;
});
}
});
}
The shortfall of this approach is the listener is running its test whenever the panel is Scrolled, could it jank the scroll? Is there a better/clearer/more performant way?
For completion I amended setScrollBehaviour to this:
void setScrollBehavior(bool _canScroll, {resetPos = false}) {
setState(() {
canScroll = _canScroll;
isDraggableBool = !_canScroll;
if (resetPos) {
slideUpPanelScrollController.jumpTo(0);
isDraggableBool = true;
}
});
}
So when the user can scroll they can't drag.
When the panel closes, resetPos == true therefore the panel scrolls to the top AND it can be dragged (slid) once more.
==== UPDATE ====
you can clone panel builder scrolling behavior and use the controller freely elsewhere. that way, you don't have to worry much about listview scroll physic or panel states.
did some cleanup, added comments, and fixed the tap/scrolling issues.
The only drawback of this method is that the user has to scroll back to the top to close the panel (you can always wrap your body with a gesture and call resetPanel on tap if needed)
import 'package:flutter/material.dart';
import 'package:sliding_up_panel/sliding_up_panel.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(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PanelController panelController = PanelController();
ScrollController? _scrollController;
/// reset the content position of your listview
void resetScrollBehavior() {
// We make sure that our scroll exist and it is attached before reset
if (_scrollController != null && _scrollController!.hasClients) {
_scrollController!.jumpTo(0);
}
}
/// close the panel and reset the scroll behavior
void resetPanel() {
// We make sure our panel is attached and open before executing
if (panelController.isAttached && panelController.isPanelOpen) {
panelController.close();
// Remove this line if you wish to not reset the scrollExtent on panel reset.
resetScrollBehavior();
}
}
void onDrawerItemTap() {
// Reset the panel and pop the screen
resetPanel();
Navigator.of(context).pop();
}
#override
void dispose() {
// we make sure to dispose our controller(s)
_scrollController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Item 1'),
onTap: () {
onDrawerItemTap();
},
),
ListTile(
title: const Text('Item 2'),
onTap: () {
onDrawerItemTap();
},
),
],
),
),
body: SlidingUpPanel(
controller: panelController,
borderRadius: BorderRadius.circular(8),
minHeight: 80,
maxHeight:
MediaQuery.of(context).size.height - AppBar().preferredSize.height,
panelBuilder: (ScrollController sc) {
// ! So we can use local scrollcontroller outside the panel builder
_scrollController ??= sc;
return ScrollingList(
scrollController: _scrollController!,
);
},
body: Center(
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
),
),
),
);
}
}
class ScrollingList extends StatefulWidget {
const ScrollingList({
Key? key,
required this.scrollController,
}) : super(key: key);
final ScrollController scrollController;
#override
_ScrollingListState createState() => _ScrollingListState();
}
class _ScrollingListState extends State<ScrollingList> {
#override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView(
controller: widget.scrollController,
children: List.generate(200, (index) => Text('Text #$index')),
),
),
);
}
}
=== OLD ===
Running some test, it seems that panelController.close();
also triggers onPanelClosed();
so you can take advantage of that to handle your scrolling behaviors.
The following sample demonstrates how you can reset your panel and/or listview by tapping on a drawer item.
import 'package:flutter/material.dart';
import 'package:sliding_up_panel/sliding_up_panel.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(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
PanelController panelController = PanelController();
ScrollController scrollController = ScrollController();
bool canScroll = false;
void onDrawerItemTap() {
if (panelController.isAttached && panelController.isPanelOpen) {
panelController.close();
}
Navigator.of(context).pop();
}
void setScrollBehavior(bool _canScroll, {resetPos = false}) {
setState(() {
canScroll = _canScroll;
if (resetPos) {
scrollController.jumpTo(0);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Item 1'),
onTap: () {
onDrawerItemTap();
},
),
ListTile(
title: const Text('Item 2'),
onTap: () {
onDrawerItemTap();
},
),
],
),
),
body: SlidingUpPanel(
controller: panelController,
borderRadius: BorderRadius.circular(8),
parallaxEnabled: false,
isDraggable: true,
onPanelOpened: () {
setScrollBehavior(true);
},
onPanelSlide: (_) {
// panel slide
},
onPanelClosed: () {
setScrollBehavior(false, resetPos: true);
},
minHeight: 80,
maxHeight:
MediaQuery.of(context).size.height - AppBar().preferredSize.height,
panelBuilder: (_) => ScrollingList(
canScroll: canScroll,
scrollController: scrollController,
),
body: Center(
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
),
),
),
);
}
}
class ScrollingList extends StatefulWidget {
const ScrollingList({
Key? key,
this.canScroll = false,
required this.scrollController,
}) : super(key: key);
final bool canScroll;
final ScrollController scrollController;
#override
_ScrollingListState createState() => _ScrollingListState();
}
class _ScrollingListState extends State<ScrollingList> {
#override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView(
controller: widget.scrollController,
physics: !widget.canScroll
? const NeverScrollableScrollPhysics()
: const AlwaysScrollableScrollPhysics(),
children: List.generate(200, (index) => Text('Text #$index')),
),
),
);
}
}
ListView might be the issue here. You could try to jump to the top before closing the panel instead of after. You could also try to provide a key to your ListView so it forces flutter to re-render. Eventually, if it doesn't work wrap the body of the slide panel inside a SingleChildScrollview and disable ListView scrolling using physics. It's a bit difficult to visualize a fix though. If you can provide a link to DartPad it would be much easier.

Is it possible to initiate a 2 dimensional array of SizedBox (as an example, I need some kind of square) in Flutter

I want to program a chess game in Flutter so first I need to make my own board. For that, I thought I can initiate a 2 dimension array of SizedBox-es (again, not necessarily) and color it just like a real chess board.
But is it possible?
You can copy paste run full code below
You can directly use package https://pub.dev/packages/flutter_chess_board or reference it's source code
code snippet of buildChessBoard
https://github.com/deven98/flutter_chess_board/blob/c8042f2aa499158c10b87aca339a9a19198ce2f3/lib/src/chess_board.dart#L182
var whiteSquareList = [
[
"a8",
"b8",
"c8",
"d8",
"e8",
"f8",
"g8",
"h8",
...
Widget buildChessBoard() {
return Column(
children: widget.whiteSideTowardsUser
? whiteSquareList.map((row) {
return ChessBoardRank(
children: row,
);
}).toList()
: whiteSquareList.reversed.map((row) {
return ChessBoardRank(
children: row.reversed.toList(),
);
}).toList(),
);
}
...
class ChessBoardRank extends StatelessWidget {
/// The list of squares in the rank
final List<String> children;
ChessBoardRank({this.children});
#override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Row(
children: children
.map(
(squareName) => BoardSquare(squareName: squareName),
)
.toList(),
),
);
}
}
working demo
full example code
import 'package:flutter/material.dart';
import 'package:flutter_chess_board/src/chess_board.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.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChessBoard(
onMove: (move) {
print(move);
},
onCheckMate: (color) {
print(color);
},
onDraw: () {},
size: MediaQuery.of(context).size.width,
enableUserMoves: true,
)
],
),
),
);
}
}
A better option is to add a gridView like this:
GridView.builder(
itemCount: 64,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
return Container(
color: index%2 == 0 ? Colors.white : Colors.black
);
},
)
If you have SizedBox instead, it will be difficult for you to add color, coin image, and alignment etc

Can I use 'index' in PageView widget in other widget?

I'm making an app divided to two sections: one(upper section) is PageView widget area, another(lower section) is Container widget area. I want the lower section to show 'we are in X page' when I change pages in the upper section.
I tried to use index of PageView widget in Container widget, but console said "undefined name 'index'".
So I declared like int index; as a global variable, and tried again, but it doesn't work. I think this index is different from index of PageView widget.
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final controller = PageController(initialPage: 0);
var scrollDirection = Axis.horizontal;
var actionIcon = Icons.swap_vert;
int index;
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
title: Text('it\'s a drill for page view'),
),
body: _buildBody(),
);
}
Widget _buildBody() {
return SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: PageView.builder(
controller: controller,
itemCount: 5,
itemBuilder: (context, index) {
return Text('it is ${index} page');
},
)
),
Expanded(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
color: Colors.blue,
child: Text('we are in ${index} page!'),
),
),
)
],
),
);
}
}
I'm a beginner of programming, and doing this as a hobby.
But I really like it. Actually I gave up my own study and career and stick to programming now. I hope you help me solve this problem.
Thank you. I love you.
yes. like controller.page for the current page.
class Sample extends StatelessWidget{
final int value;
Sample(this.value);
build(context) => Text("you are in $value");
}
and use Sample(controller.page)
EDIT: your code should be
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final controller = PageController(initialPage: 0);
var scrollDirection = Axis.horizontal;
var actionIcon = Icons.swap_vert;
int currentPage=0;
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
title: Text('it\'s a drill for page view'),
),
body: _buildBody(),
);
}
Widget _buildBody() {
return SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: PageView.builder(
controller: controller,
itemCount: 5,
itemBuilder: (context, index) {
return Text('it is ${index} page');
},
onPageChanged: (page){
setState(() {
currentPage=page;
});
},
)
),
Expanded(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
color: Colors.blue,
child: Text('we are in ${currentPage} page!'),
),
),
)
],
),
);
}
}
Just add listener to PageController like that:
#override
void initState() {
super.initState();
index = 0;
controller.addListener(() {
setState(() {
index = controller.page.toInt();
});
});
}

Flutter Web persistent header

With the introduction of flutter for web it has me trying to achieve a website style header that is persistent when using routes and across the entire app. Appbar doesn't appear to be the solution since each scaffold has its own appBar. I've created the header widget that's in a Column with the MaterialApp. However, this implementation feels wrong as everything should be a child of MaterialApp or CupertinoApp.
If the searchBar header can be placed within the MaterialApp and I'm able to use Navigator that's would be preferred. I'm really here for guidance and the "right" way to do this.
void main() {
initKiwi();
// BlocSupervisor().delegate = AppBlocDelegate();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Material(
elevation: 2.0,
color: Colors.white,
child: MediaQuery(
data: MediaQueryData.fromWindow(ui.window),
child: Directionality(
textDirection: TextDirection.ltr,
child: Container(
height: 50,
child: SearchBar(),
),
),
),
),
Expanded(
child: MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl',
),
home: Text("Pages & Routes Here"),
),
),
]);
}
}
Though it's not using routes I was able to solve this using IndexedStack. This also preserves any scrolling I've done in the ProductsPage() when closing the search page. The AppBar is persistent and was able to keep the code to a minimum.
main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl'
),
home: MainPage(),
);
}
}
main_page.dart
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _searchBloc = kiwi.Container().resolve<SearchBloc>();
final _productsBloc = kiwi.Container().resolve<ProductsBloc>();
PageController pageController;
int currentPage = 0;
void _onSearchActive({bool isActive}) {
setState(() {
this.currentPage = isActive ? 1 : 0;
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: _buildScaffold(),
);
}
Widget _buildScaffold() {
return BlocProviderTree(
blocProviders: [
BlocProvider<SearchBloc>(bloc: _searchBloc),
BlocProvider<ProductsBloc>(bloc: _productsBloc),
],
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: SearchBar(onIsActive: _onSearchActive),
),
body: IndexedStack(
children: [
ProductsPage(),
SearchPage(),
],
index: currentPage,
),
),
);
}
}

Flutter googlemaps reloads everytime I change page in tabnavigator

Hello, I'm trying to use Flutter and the new googlemaps plugin in my application. My problem is that everytime I change page in the tabnav I it will reload googlemaps widget. I tried using AutomaticKeepAliveClientMixin but that didn't help. I will keep trying to figure this out, but help is appreciated or if somebody knows what I'm doing wrong? Thank you!
This is my code:
import 'package:flutter/material.dart';
import 'package:restapoints/pages/pages.dart';
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Flutter App',
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomeState();
}
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
final List<Widget> _children = [
MapPage(),
QrPage(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Flutter App'),
),
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Messages'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Profile'))
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
MapPage:
class MapPage extends StatefulWidget {
#override
_MapPageState createState() => _MapPageState();
}
class _MapPageState extends State<MapPage>
with AutomaticKeepAliveClientMixin<MapPage> {
#override
Widget build(BuildContext context) {
return Container(
///Getting size of the screen from [MediaQuery] inherited widget.
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: _onMapCreated,
options: GoogleMapOptions(
compassEnabled: true,
mapType: MapType.normal,
trackCameraPosition: true,
),
),
///If not enabled don't show ListView of places.
toggleListView
? Positioned(
top: MediaQuery.of(context).size.height / 2,
left: 10,
child: Container(
height: MediaQuery.of(context).size.height / 4,
width: MediaQuery.of(context).size.width,
child: ListView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.all(5.0),
children: _places.map((place) {
return _placeCard(place);
}).toList(),
)),
)
: Container()
],
),
);
#override
bool get wantKeepAlive => true;
}
Instead of making the page a StatefulWidget like MapPage, I added the Widget inside an IndexedStack to prevent rebuild of the page. Though the downside of this approach is that the page might take time to build if there's a lot of pages added on Stack.