I'm trying to build a app using Flutter with GetX for State Management, and in one of my pages i need to build a TabView widget in the middle of the page, i already found a lot of stuff explaining how to build a TabView as widget, as this post and this article, but all of these extends a State controller with a SingleTickerProviderStateMixin.
As i understand reading the documentation, i'm not supposed to use StatefulWidgets and States like that, but i cant figure out how to elaborate a solution using the GetX architecture.
I already tried in my page things like:
class CourseDetailPage extends StatelessWidget {
final TabController _tabController = Get.put(TabController(vsync: null, length: 2));
}
and
class CourseDetailPage extends StatelessWidget {
final TabController _tabController = TabController(vsync: null, length: 2);
}
But the VSYNC argument for the TabController cannot be null, and i don't figure out how i cant obtain a TickerProvider to populate it.
Would the following be a solution to using a GetX controller to control a TabView?
GetTicker
There's a Get version of SingleTickerProviderMixin which implements the TickerProvider interface (using the same Ticker class from Flutter SDK).
It has a catchy name:
GetSingleTickerProviderStateMixin
(Updated 21-12-20: SingleGetTickerProviderMixin has been deprecated with latest GetX. Thanks to commenter pointing this out.)
The below example is basically from the Flutter docs for TabController.
From the linked example's StatefulWidget I transplanted its contents into a GetxController (MyTabController) and added mixin of SingleGetTickerProviderMixin:
class MyTabController extends GetxController with GetSingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
late TabController controller;
#override
void onInit() {
super.onInit();
controller = TabController(vsync: this, length: myTabs.length);
}
#override
void onClose() {
controller.dispose();
super.onClose();
}
}
To use MyTabController inside a Stateless widget:
class MyTabbedWidget extends StatelessWidget {
const MyTabbedWidget();
#override
Widget build(BuildContext context) {
final MyTabController _tabx = Get.put(MyTabController());
// ↑ init tab controller
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabx.controller,
tabs: _tabx.myTabs,
),
),
body: TabBarView(
controller: _tabx.controller,
children: _tabx.myTabs.map((Tab tab) {
final String label = tab.text!;
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
);
}
}
Here's the rest of the app example to just copy & paste into Android Studio / VisualStudio Code to run:
import 'package:flutter/material.dart';
import 'package:get/get.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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX Tab Example'),
),
body: Column(
children: [
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text('Some random stuff'),
),
),
Expanded(
flex: 4,
child: MyTabbedWidget(),
)
],
),
);
}
}
class MyTabController extends GetxController with GetSingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
late TabController controller;
#override
void onInit() {
super.onInit();
controller = TabController(vsync: this, length: myTabs.length);
}
#override
void onClose() {
controller.dispose();
super.onClose();
}
}
class MyTabbedWidget extends StatelessWidget {
const MyTabbedWidget();
#override
Widget build(BuildContext context) {
final MyTabController _tabx = Get.put(MyTabController());
// ↑ init tab controller
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabx.controller,
tabs: _tabx.myTabs,
),
),
body: TabBarView(
controller: _tabx.controller,
children: _tabx.myTabs.map((Tab tab) {
final String label = tab.text!;
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
);
}
}
As you said in all articles they extend State with SingleTickerProviderStateMixin because you won't be able to initialize your TabController outside of a State as TabController manage a state (doc).
A workaround would be to not use a variable for your controller and wrap your TabBar and TabView widget tree inside a DefaultTabController.
Here is an example from the official doc:
class MyDemo extends StatelessWidget {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: myTabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: myTabs,
),
),
body: TabBarView(
children: myTabs.map((Tab tab) {
final String label = tab.text.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
),
);
}
}
By doing so you won't need to have your TabView inside a State but you won't be using GetX either.
I came across the exact issue and it shows that the GetX framework is not ready for many possible scenarios and you should use it cautiously.
The solution I came up with is a wrapper widget. In the proposed example I used HookWidget but you are free to use Stateful widget as well.
class TabWidget extends HookWidget {
final List<Widget> children;
final RxInt currentTab;
final int initialIndex;
TabWidget({
#required this.children,
#required this.currentTab,
#required this.initialIndex,
});
#override
Widget build(BuildContext context) {
final controller = useTabController(
initialLength: children.length,
initialIndex: initialIndex,
);
currentTab.listen((page) {
controller.animateTo(page);
});
return TabBarView(controller: controller, children: children);
}
}
As you can see there is a Rx variable which controls the state of the tabView. So you have to pass a RxInt from the outside and whenever the value of it changes, the tabView updates respectively.
class TestView extends GetView<TestController> {
#override
Widget build(BuildContext context) {
final screen = 0.obs;
return SafeArea(
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.forward),
onPressed: () => screen.value = screen.value + 1,
)
],
),
body: TabWidget(
initialIndex: 1,
currentTab: screen,
children: [
child1,
child2,
child3,
...,
],
),
),
);
}
Right now we take care of the controller by the help of HookWidget. If you are using Stateful widget you have to dispose it correctly.
Related
Hi,
I am making a to-do/tasks app, I want the users to create their own categories/tabs for them to store notes under, I already can show the notes with the corresponding category but I'm stuck at actually displaying data from firebase as tabs in TabBar, I've created a List with hardcoded values, how do I get values from Firebase into my list and iterate them as actual tabs?
class HomeTopTabs extends StatefulWidget {
const HomeTopTabs({super.key});
#override
State<HomeTopTabs> createState() => _HomeTopTabs();
}
class _HomeTopTabs extends State<HomeTopTabs>
with SingleTickerProviderStateMixin {
final Stream<QuerySnapshot> categories =
FirebaseFirestore.instance.collection('categories').snapshots();
static const List<Tab> myTabs = <Tab>[
Tab(text: 'Spanish'),
Tab(text: 'Work'),
];
late TabController _tabController;
late final FirebaseCloudStorage _notesService;
String get userId => AuthService.firebase().currentUser!.id;
#override
void initState() {
super.initState();
_notesService = FirebaseCloudStorage();
_tabController = TabController(length: myTabs.length, vsync: this);
// _tabController.addListener(_handleTabSelection);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
height: 500,
child: Scaffold(
appBar: AppBar(
flexibleSpace: SafeArea(
child: TabBar(
controller: _tabController,
isScrollable: true,
onTap: (index) {},
tabs: myTabs,
)),
),
body: TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
final String label = tab.text!.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
),
);
}
}
I have tried to search other questions and relating tutorials but most of them is depreciated code.
I have a strange issue with my TabBarView and Tabs and that is that appBar title of first screen appears on other screen when using TabBarView. I only want appBar title from screen1 to be on screen1 and appBar title in screen2 be on screen2. Screen1 and Screen2 are same.
How do I fix this error?
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: TabBarDemo(),
);
}
}
class TabBarDemo extends StatefulWidget {
#override
_TabBarDemoState createState() => _TabBarDemoState();
}
class _TabBarDemoState extends State<TabBarDemo>
with SingleTickerProviderStateMixin {
TabController tabController;
List<Tab> tabBars;
List<Widget> tabBarViews;
#override
void initState() {
// TODO: implement initState
super.initState();
tabController = new TabController(vsync: this, length: 3);
tabBars = [
Tab(icon: Icon(Icons.home, size: 34), text: 'Hem',),
Tab(icon: Icon(Icons.search, size: 34), text: 'Sök',),
Tab(icon: Icon(Icons.settings, size: 34), text: 'Inställningar'),
];
tabBarViews = [welcomeLayout(context),Screen1(),Screen2()];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: ColorStyle.black,
elevation: 0,
automaticallyImplyLeading: false,
title: Text('Home Screen'),
centerTitle: true,
),
body: TabBarView(
children: tabBarViews,
controller: tabController,
),
bottomNavigationBar: TabBar(controller: tabController, tabs: tabBars,
labelColor: ColorStyle.white,
),
);
}
}
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: ColorStyle.black,
elevation: 0,
title: Text('First Screen'),
centerTitle: true,
),
backgroundColor: ColorStyle.black,
body: Container()
),
);
}
}
Actually its not the appbar of first screen being shown. It is the one that you have mentioned in your TabBarDemo. So to get rid of it remove it from their i.e.-
#override
Widget build(BuildContext context) {
return Scaffold(
//removed appBar
body: TabBarView(
children: tabBarViews,
controller: tabController,
),
bottomNavigationBar: TabBar(controller: tabController, tabs: tabBars,
labelColor: ColorStyle.white,
),
);
}
}
and if you want to show it in your welcomeLayout then make it a scaffold widget and show appbar over there.
I have design like above , my tabbar is inside BottomNavigationBar and i want place the tabbar in my BottomNavigationBar menu. The problem is , i don't want have nested scaffold and i don't want use TabController because my tabs is dynamic , tabs length can be decrease or increase depending of user adding it. If i use TabController and define it inside initState , the tabs can't be increase/decrease because in initState only once define the value.
How can i do this ?
My expectation similiar in below example , when i adding tab and tabMenu it will increase . But get this error
The following assertion was thrown building TabBar(dirty, dependencies: [_InheritedTheme,
_LocalizationsScope-[GlobalKey#a56a4]], state: _TabBarState#fb337):
Controller's length property (2) does not match the number of tabs (3) present in TabBar's tabs
property.
The relevant error-causing widget was:
TabBar
Dummy Example
class TaskScreen extends StatefulWidget {
#override
_TaskScreenState createState() => _TaskScreenState();
}
class _TaskScreenState extends State<TaskScreen> with SingleTickerProviderStateMixin {
TabController _tabController;
final List<Tab> _tab = [
Tab(text: 'Sunday'),
Tab(text: 'Monday'),
];
final List<Widget> _tabMenu = [
Text('This is Sunday'),
Text('This is Monday'),
];
#override
void initState() {
super.initState();
_tabController = TabController(length: _tab.length, vsync: this);
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FlatButton(
onPressed: () {
setState(() {
_tab.add(Tab(
text: '1',
));
_tabMenu.add(Text('1'));
});
print('tab ${_tab.length}');
print('tabMenu ${_tabMenu.length}');
},
child: Text('add')),
Container(
color: colorPallete.primaryColor,
padding: EdgeInsets.only(top: sizes.statusBarHeight(context)),
child: TabBar(
tabs: _tab.map((e) => e).toList(),
controller: _tabController,
),
),
Expanded(
child: TabBarView(
children: _tabMenu.map((e) => e).toList(),
controller: _tabController,
),
)
],
);
}
}
use TickerProviderStateMixin instead of SingleTickerProviderStateMixin
and change TabController on changing tabs number;
add isScrollable: true when you don't have space for all your tabs
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: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return TaskScreen();
}
}
class TaskScreen extends StatefulWidget {
#override
_TaskScreenState createState() => _TaskScreenState();
}
class _TaskScreenState extends State<TaskScreen> with TickerProviderStateMixin {
TabController _tabController;
final List<Tab> _tab = [
Tab(text: 'Sunday'),
Tab(text: 'Monday'),
];
final List<Widget> _tabMenu = [
Text('This is Sunday'),
Text('This is Monday'),
];
#override
void initState() {
super.initState();
_tabController = TabController(length: _tab.length, vsync: this);
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FlatButton(
onPressed: () {
setState(() {
_tab.add(
Tab(
text: '1',
),
);
_tabMenu.add(
Text('1'),
);
_tabController =
TabController(length: _tab.length, vsync: this);
});
print('tab ${_tab.length}');
print('tabMenu ${_tabMenu.length}');
},
child: Text('add')),
Container(
color: Colors.blue,
padding: EdgeInsets.only(top: 10),
child: TabBar(
// isScrollable: true,
tabs: _tab.map((e) => e).toList(),
controller: _tabController,
),
),
Expanded(
child: TabBarView(
children: _tabMenu.map((e) => e).toList(),
controller: _tabController,
),
)
],
);
}
}
In flutter implementing a tab layout is easy and straightforward. This is a simple example from the official documentation:
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Icon(Icons.directions_car),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
But here is the thing, I want to get the active tab index so I can apply some logic on certain tabs. I search the documentation but I wasn't able to figure it out. Can you guys help and thanks?
The whole point of DefaultTabController is for it to manage tabs by itself.
If you want some custom tab management, use TabController instead.
With TabController you have access to much more informations, including the current index.
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => new _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage>
with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
new Tab(text: 'LEFT'),
new Tab(text: 'RIGHT'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: new TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
return new Center(child: new Text(tab.text));
}).toList(),
),
);
}
}
In this case, using StatefulWidget and State isn't a good idea.
You can get current index by DefaultTabController.of(context).index;.
Follow the code:
...
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(~), Tab(~)
]
),
actions: [
// At here you have to get `context` from Builder.
// If you are not sure about this, check InheritedWidget document.
Builder(builder: (context){
final index = DefaultTabController.of(context).index;
// use index at here...
})
]
)
You can access the current index when the tab is selected by onTap event of TabBar.
TabBar(
onTap: (index) {
//your currently selected index
},
tabs: [
Tab1(),
Tab2(),
]);
Just apply a listener on the TabController.
// within your initState() method
_tabController.addListener(_setActiveTabIndex);
void _setActiveTabIndex() {
_activeTabIndex = _tabController.index;
}
Use DefaultTabController you can get current index easily whether the user changes tabs by swiping or tap on the tab bar.
Important: You must wrap your Scaffold inside of a Builder and you can then retrieve the tab index with DefaultTabController.of(context).index inside Scaffold.
Example:
DefaultTabController(
length: 3,
child: Builder(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
bottom: TabBar(
isScrollable: true,
tabs: [Text('0'), Text('1'), Text('2')]),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
print(
'Current Index: ${DefaultTabController.of(context).index}');
},
),
);
}),
),
New working solution
I'd suggest you to use TabController for more customisations. To get active tab index you should use _tabController.addListener and _tabController.indexIsChanging.
Use this full code snippet:
class CustomTabs extends StatefulWidget {
final Function onItemPressed;
CustomTabs({
Key key,
this.onItemPressed,
}) : super(key: key);
#override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabs>
with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
TabController _tabController;
int _activeIndex = 0;
#override
void initState() {
super.initState();
_tabController = TabController(
vsync: this,
length: myTabs.length,
);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
setState(() {
_activeIndex = _tabController.index;
});
}
});
return Container(
color: Colors.white,
child: TabBar(
controller: _tabController,
isScrollable: true,
indicatorPadding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(10.0), color: Colors.green),
tabs: myTabs
.map<Widget>((myTab) => Tab(
child: Container(
width: width / 3 -
10, // - 10 is used to make compensate horizontal padding
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color:
_activeIndex == myTabs.indexOf(myTab)
? Colors.transparent
: Color(0xffA4BDD4),
),
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
child: Align(
alignment: Alignment.center,
child: Text(
myTab.text,
style: TextStyle(color: Colors.white),
),
),
),
))
.toList(),
onTap: widget.onItemPressed,
),
);
}
}
Thanks to the example of Rémi Rousselet, you can do it, the code like this:
_tabController.index
This will return the current index of the position of your TabBarView
You can add a listener to listen to changes in tabs like below
tabController = TabController(vsync: this, length: 4)
..addListener(() {
setState(() {
switch(tabController.index) {
case 0:
// some code here
case 1:
// some code here
}
});
});
Well, nothing here was working in my case.
I tried several responses so as a result i used a provider to keep and retrieve the current index selected.
First the model.
class HomeModel extends ChangeNotifier {
int _selectedTabIndex = 0;
int get currentTabIndex => _selectedTabIndex;
setCurrentTabIndex(final int index){
_selectedTabIndex = index;
// notify listeners if you want here
notifyListeners();
}
...
}
Then i used _tabController.addListener() to update my model.
class HomePageState extends State<HomeScreen> {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: _tabs.length);
_tabController.addListener(() {
context.read<HomeModel>().setCurrentTabIndex(_tabController.index);
});
}
...
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabs.length,
child :
Scaffold(
backgroundColor: Colors.white70,
appBar: AppBar(
/*iconTheme: IconThemeData(
color: Colors.black
),*/
bottom: TabBar(
controller: _tabController,
tabs: _tabs,
),
title: Text(_getAppBarTitle(),style: const TextStyle(/*color: Colors.red,*/fontSize: 22.0),)
...
)
)
);
}
...
}
Finally last but not least retrieve value when you need.
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Consumer<HomeModel>(
builder: (context, homeModel, child) {
return Text(homeModel.currentTabIndex); // herre we get the real current index
});
}
}
Set the variable in top.
class _MainTabWidgetState extends State<MainTabWidget> {
#override void initState() {
// TODO: implement initState
super.initState();
}
int selected_index = 0;
}
Now set index in Tabbar onTap
onTap: (index) {
setState(() {
selected_index = index;
});
},
This Code will give you index of Active tab , also save the tab index for future use, and when you back to the tab page the the previous active page will be displayed.
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
TabScope _tabScope = TabScope.getInstance();
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
index: _tabScope.tabIndex, //
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
onTap: (index) => _tabScope.setTabIndex(index), //current tab index
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Icon(Icons.directions_car),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
class TabScope{ // singleton class
static TabScope _tabScope;
int tabIndex = 0;
static TabScope getInstance(){
if(_tabScope == null) _tabScope = TabScope();
return _tabScope;
}
void setTabIndex(int index){
tabIndex = index;
}
}
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(