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.
Related
I have a Dart file named page0.dart and this only includes a BottomNavigationBar.
BottomNavigationBar has 2 items in it which redirects me to dashboard.dart and target.dart, the navigation via the BottomNavigationBar works as expected.
Now the problem: I need a button on dashboard.dart that should redirect me to target.dart, but keep the ButtomNavigationBar visible.
I am redirecting with Navigator.push, but that opens target.dart directly and skips page0.dart I think.
Screenshots are below. Please watch them for better understanding my problem.
Here are the code samples:
page0.dart:
import 'package:flutter/material.dart';
import 'package:navbartest/dashboard.dart';
import 'package:navbartest/target.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key, required String title}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return const Scaffold(
bottomNavigationBar: BottomNavBar(),
);
}
}
class BottomNavBar extends StatefulWidget {
const BottomNavBar({super.key});
#override
State<BottomNavBar> createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _pageIndex = 0;
final List<Widget> _tabList = const [
Dashboard(),
Target(),
];
Widget? onItemTap(int index) {
setState(() {
_pageIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
_tabList.elementAt(_pageIndex),
Padding(
padding: EdgeInsets.only(right: 35, bottom: 25, left: 35),
child: Align(
alignment: const Alignment(0.0, 1.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
child: BottomNavigationBar(
backgroundColor: const Color(0xff565656),
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
unselectedItemColor: Colors.white,
selectedItemColor: Colors.white,
onTap: onItemTap,
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.home),
label: "Dashboard",
),
BottomNavigationBarItem(
icon: const Icon(Icons.car_repair),
label: "Target",
),
],
),
),
),
),
],
);
}
}
dashboard.dart
import 'package:navbartest/target.dart';
class Dashboard extends StatelessWidget {
const Dashboard({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Container(
width: 120,
height: 20,
color: Colors.blue,
child: InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Target()),
);
},
child: Text('navigate to target'),
),
),
),
),
);
}
}
target.dart:
class Target extends StatelessWidget {
const Target({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text('target'),
),
);
}
}
when the app is started, it looks like this
when I click the blue button to navigate, it looks like this (NavBar is gone!)
when I click the symbol in the navbar redirecting me to target.dart, it looks like this (thats how I want it with the blue button too!)
actually you need to use a state management for this type of actions , but I found a work around in your case ,
I will set the classes next Just replace them with your classes and it will work.
1 - page0.dart:
import 'target.dart';
import 'package:flutter/material.dart';
import 'dash.dart';
class BottomNavBar extends StatefulWidget {
const BottomNavBar({super.key});
#override
State<BottomNavBar> createState() => BottomNavBarState();
}
class BottomNavBarState extends State<BottomNavBar> {
late int _pageIndex;
late final List<Widget> _tabList;
Widget? onItemTap(int index) {
setState(() {
_pageIndex = index;
});
return null;
}
#override
void initState(){
super.initState();
_pageIndex = 0;
_tabList = [
Dashboard(ref:(int number){
setState(() {
_pageIndex = number;
});
}),
const Target(),
];
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
_tabList.elementAt(_pageIndex),
Padding(
padding: EdgeInsets.only(right: 35, bottom: 25, left: 35),
child: Align(
alignment: const Alignment(0.0, 1.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
child: BottomNavigationBar(
backgroundColor: const Color(0xff565656),
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
unselectedItemColor: Colors.white,
selectedItemColor: Colors.white,
onTap: onItemTap,
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.home),
label: "Dashboard",
),
BottomNavigationBarItem(
icon: const Icon(Icons.car_repair),
label: "Target",
),
],
),
),
),
),
],
);
}
}
2 - dashboard.dart :
import 'package:flutter/material.dart';
class Dashboard extends StatelessWidget {
const Dashboard({super.key, required this.ref});
final Function(int)? ref ;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Container(
width: 120,
height: 20,
color: Colors.blue,
child: InkResponse(
onTap: ()=>ref!(1),
child: Text('navigate to target'),
),
),
),
),
);
}
}
3 - target.dart:
import 'package:flutter/material.dart';
class Target extends StatelessWidget {
const Target({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text('target'),
),
);
}
}
remove the import and re import for the right paths in your application file , but this is a work around and you should use the state management .
I have a problem with my BottomNavigationBar in Flutter.
Please help me with this issue. i really need it to done.
I don't want to keep my page alive if I press jump to any page from screens, not from my BottomNavigationBar.
eg, if I have three screens that navigate from the bottom bar its works fine but if I add a button in any of the three pages that navigate to another screen that does not belong to the bottom bar then it keeps that screen alive the previous screen.
here my implementation.
BottomNavigation :
footer.dart
import 'package:flutter/material.dart';
import 'MyPage.dart';
import 'MyPage2.dart';
import 'MyPage3.dart';
import 'package:double_back_to_close_app/double_back_to_close_app.dart';
import 'Notifications.dart';
void main() {
runApp(MaterialApp(
home: Footer(),
));
}
class Footer extends StatefulWidget {
#override
_Footer createState() => _Footer();
}
class _Footer extends State<Footer> {
late List<Widget> _pages;
List<BottomNavigationBarItem> _items = [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "Home",
),
BottomNavigationBarItem(
icon: Icon(Icons.messenger_rounded),
label: "Messages",
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: "Settings",
)
];
late int _selectedPage;
#override
void initState() {
super.initState();
_selectedPage = 0;
_pages = [
MyPage(
count: 1,
),
MyPage2(
count: 2,
),
MyPage3(
count: 3,
),
// This avoid the other pages to be built unnecessarily
//Notifications(),
// SizedBox(),
];
}
late DateTime currentBackPressTime;
#override
Widget build(BuildContext context) {
return Scaffold(
body: DoubleBackToCloseApp(
snackBar: SnackBar(
content: const Text(
'Tap back again to leave',
style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal),
),
duration: Duration(seconds: 4),
backgroundColor: Colors.blue,
width: 340.0,
padding: EdgeInsets.all(15),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
child: _pages[_selectedPage],
),
bottomNavigationBar: BottomNavigationBar(
items: _items,
currentIndex: _selectedPage,
onTap: (index) {
setState(() {
// now check if the chosen page has already been built
// if it hasn't, then it still is a SizedBox
_selectedPage = index;
});
},
)
);
}
}
MyPage.dart:
import 'package:flutter/material.dart';
import 'MyCustomPage.dart';
import 'Notifications.dart';
class MyPage extends StatefulWidget {
final count;
MyPage({Key? key, this.count}) : super(key: key);
#override
_MyPage createState() => _MyPage();
}
class _MyPage extends State<MyPage>{
#override
Widget build(BuildContext context) {
// You'll see that it will only print once
return Navigator(
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.text),
actions: <Widget>[
Row(
children: [
InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (ctx) => Notifications()));
},
child: Icon(Icons.notifications),
)
],
)
],
),
body: Center(
child: RaisedButton(
child: Text('my page1'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (ctx) => MyCustomPage()));
},
),
),
);
},
);
},
);
}
}
MyCustomPage.dart
import 'package:flutter/material.dart';
class MyCustomPage extends StatefulWidget {
MyCustomPage ({Key? key}) : super(key: key);
#override
_MyCustomPage createState() => _MyCustomPage();
}
class _MyCustomPage extends State<MyCustomPage>{
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: AppBar(
title: Text('custompage'),
),
body: Column(
children: [
Expanded(
child: Container(
child: ListView.builder(
itemCount: 15,
itemBuilder: (context, index) {
return Container(
width: double.infinity,
child: Card(
child: Center(
child: Text('My Custom Page'),
),
),
);
},
),
),
),
],
),
);
}
}
Here are my three files that navigate the inner screen
the flow of navigation is first load Footer.dart file that is the main file inside there is three pages in BottomNavigationBar() in the first screen on tap home icon it loads MyPage.dart file which contains a text and button. on the button press, it navigates MyCustomPage.dart file. now the issue is when I click on the home icon from BottomNavigationBar() it loads MyPage screen and on press button inside that screen it loads MyCustomPage but when MyCustomPage.dart file load it keeps home icon alive and I don't have access to press the home icon again.
I hope you understand what I am trying to say.
If anyone knows please help me.
I'm new at Flutter and currently trying very simple exercises to understand the basics. I have just created a basic Home with a BottomNavigationBar at, well, the screen bottom. But when I tap a bar icon, it doesn't switch colors to appear as if it was tapped. I need to reload it to update it. I can't update the icon state from 'MyBottmNavigationBar' class... If I simply copy/paste Flutter.dev sample code it works... What I need to fix in my code?
Feel free to point any errors or tips!
Thanks in advance!
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Home();
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyScaffold(),
);
}
}
class MyScaffold extends StatefulWidget {
#override
_MyScaffoldState createState() => _MyScaffoldState();
}
class _MyScaffoldState extends State<MyScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hey Flutter'),),
body: MyListView(),
bottomNavigationBar: MyBottomNavigationBar(),
);
}
}
class MyListView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(2),
children: <Widget>[
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: const Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
],
);
}
}
class MyBottomNavigationBar extends StatefulWidget {
MyBottomNavigationBar({Key key}) : super(key: key);
#override
_MyBottomNavigationBarState createState() => _MyBottomNavigationBarState();
}
class _MyBottomNavigationBarState extends State<MyBottomNavigationBar> {
int _selectedIndex = 0;
void _onItemTapped(int index){
_selectedIndex = index;
print('Selected: ${_selectedIndex} CurrentIndex: ${index}');
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: _selectedIndex,
selectedItemColor: Colors.pink,
onTap: _onItemTapped,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.table_chart),
title: Text('Projects'),
),
BottomNavigationBarItem(
icon: Icon(Icons.people),
title: Text('Teams'),
),
],
);
}
}
```
Also in addition don't write the ontapped() in the build method as it was creates the same instance and never updates the view.
You forgot to call setState in your _onItemTapped function.
To fix this problem, replace the code below with your _onItemtapped function:
void _onItemTapped(int index){
// add the setstate callback here
setState(() {
_selectedIndex = index;
});
print('Selected: ${_selectedIndex} CurrentIndex: ${index}');
}
I hope this helps.
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(
Newsvoice got a scrollable tabs on the bottom of the screen on top of a bottom bar. How can I implement this UI ?
Thanks.
Here is some example code that uses a Column to position a scrollable TabBar and a BottomNavigationBar in the bottomNavigationBar slot of the Scaffold. Note that the tabs disappear when you select the second ("motorcycle") screen, using an AnimatedCrossFade.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Navigation Example',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
const List<String> tabNames = const<String>[
'foo', 'bar', 'baz', 'quox', 'quuz', 'corge', 'grault', 'garply', 'waldo'
];
class _MyHomePageState extends State<MyHomePage> {
int _screen = 0;
#override
Widget build(BuildContext context) {
return new DefaultTabController(
length: tabNames.length,
child: new Scaffold(
appBar: new AppBar(
title: new Text('Navigation example'),
),
body: new TabBarView(
children: new List<Widget>.generate(tabNames.length, (int index) {
switch (_screen) {
case 0: return new Center(
child: new Text('First screen, ${tabNames[index]}'),
);
case 1: return new Center(
child: new Text('Second screen'),
);
}
}),
),
bottomNavigationBar: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
new AnimatedCrossFade(
firstChild: new Material(
color: Theme
.of(context)
.primaryColor,
child: new TabBar(
isScrollable: true,
tabs: new List.generate(tabNames.length, (index) {
return new Tab(text: tabNames[index].toUpperCase());
}),
),
),
secondChild: new Container(),
crossFadeState: _screen == 0
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: const Duration(milliseconds: 300),
),
new BottomNavigationBar(
currentIndex: _screen,
onTap: (int index) {
setState(() {
_screen = index;
});
},
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.airplanemode_active),
title: new Text('Airplane'),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.motorcycle),
title: new Text('Motorcycle'),
),
],
),
],
),
),
);
}
}