Im having a Scaffold with a bottomSheet and a bottomNavigationBar in a stateful widget.
Within the bottomSheet, i plan to add buttons, calling setState and triggering a rebuild of the local widget tree.
The problem is, that there is a strange animation happening when calling setState or when rebuilding the widgets. The bottom sheet appears to come from the bottom on top of the bottomNavigationbar.
Is there a way to fix that, such that this animation does not happen, when rebuilding or calling setState?
Please see the code below. To observe the behaviour, run the code and click on the green bottomSheet:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomSheet: MaterialButton(onPressed: () {
setState(() {});
}, child: Container(height: 50, color: Colors.green)),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
You should be able to reproduce the behavior i'm talking about when copy/pasting and running the code above.
I'm not sure what i'm doing wrong here, but this appears quite fishy to me.
Related
I am using flutter. I would like to navigate between pages of bottom nav-bar from inside the body of page.
Not to create a new screen on top of it. And carry data between pages.
I am using classic bottom navbar in flutter with pages in body of scaffold.
Something like this.
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: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
I tried pageview but with that I can switch between pages only on swipe and cant send data between them.
you can also get that widget from other places with A GlobalKey :
First, create a GlobalKey in the global scope ( make it outside you StatefulWidegt class )
final glbKey = GlobalKey();
then assign that key to BottomNavigationBar :
bottomNavigationBar: BottomNavigationBar(
key: glbKey,
items: const <BottomNavigationBarItem>[
// ...
Now, you can call it from your onPressed method like this:
onPressed: () {
//...
BottomNavigationBar navigationBar = glbKey.currentWidget as BottomNavigationBar;
navigationBar.onTap!(2); // change 2 with index of your target screen
}
you can simply simulate running the _onItemTapped() with the index of your target navigation item, in your search button icon onPressed, add this:
onPressed: () {
//...
_onItemTapped(2); // replace 2 with you screen index.
}
this will navigate to the search page programmatically.
I'm creating a simple app consisting of three pages, the only thing to be changed when using the navigation bar is the body. I need every body to be in it's own dart file. whenever I try achieving this and replace the text with another widget, I break the app.
Here's my code so far:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Sample App';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 2: Account',
style: optionStyle,
),
Text(
'Index 1: Locate a Store',
style: optionStyle,
),
Text(
'Index 0: Home',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
backgroundColor: Colors.amber[400],
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false, // <-- HERE
showUnselectedLabels: false, // <-- AND HERE
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.manage_accounts),
label: 'Account',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Store Locator',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home Page',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amberAccent[400],
onTap: _onItemTapped,
iconSize: 35,
),
);
}
}
To sum it up, The Text widget need to be replaced with new body/container that's saved in its own dart file, and work with the navigation bar.
I'm trying to make a Bottom Navigation Bar in Flutter but it keeps showing a shadow in front of the Navigation Bar. The code below is the code that taken from the flutter websites which has guarantee 100% work. But the fact is I keep failing because it doesn't work well on my Flutter. Is the version/SDK the problem?
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Set elevation: 0.0, Inside your BottomNavigationBar widget
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
//backgroundColor: Theme.of(context).primaryColor,
elevation: 0.0,
In my project, I used indexing for my BottomNavigationBar items. It controlled all the three main screen I have (Home, Ticketing, & Profile). Literally, I just used the bottom navigation buttons to navigate between the screens. As alternative way, I wanted to custom a button which located in Home screen. So that when it tapped, it will navigate to the Ticketing (2nd screen).
I tried to use Navigator.pushNamed(context, screenName);, but it will stack above all the screens and not showing the BottomNavigationBar.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyNavigationBar (),
);
}
}
class MyNavigationBar extends StatefulWidget {
MyNavigationBar ({Key key}) : super(key: key);
#override
_MyNavigationBarState createState() => _MyNavigationBarState();
}
class _MyNavigationBarState extends State<MyNavigationBar > {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
Text('Home Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
Text('Search Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
Text('Profile Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter BottomNavigationBar Example'),
backgroundColor: Colors.green
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
backgroundColor: Colors.green
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
backgroundColor: Colors.yellow
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile'),
backgroundColor: Colors.blue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
iconSize: 40,
onTap: _onItemTapped,
elevation: 5
),
);
}
}
in _onItemTapped(int index) method just change your view index like, better to you make one controller class you you can achive any othe widget or class.
void _onItemTapped() {
setState(() {
_selectedIndex = 1;
});
}
How do I extract BottomNavigationBar widget from the Flutter's example, please? The problematic part for me is the _onItemTapped function that needs to be used in the BottomNavigationBar as well as in the Scaffold
This is Flutter's BottomNavigationBar example:
/// Flutter code sample for BottomNavigationBar
// This example shows a [BottomNavigationBar] as it is used within a [Scaffold]
// widget. The [BottomNavigationBar] has three [BottomNavigationBarItem]
// widgets and the [currentIndex] is set to index 0. The selected item is
// amber. The `_onItemTapped` function changes the selected item's index
// and displays a corresponding message in the center of the [Scaffold].
//
// ![A scaffold with a bottom navigation bar containing three bottom navigation
// bar items. The first one is selected.](https://flutter.github.io/assets-for-api-docs/assets/material/bottom_navigation_bar.png)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
You can extract the BottomNavigationBar as a widget & handle the _onItemTapped as a callback as shown below:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: CustomBottomNavigation(
selectedIndex: _selectedIndex,
onTap: _onItemTapped,
),
);
}
}
Create another file called custom_bottom_navigation.dart with the following code:
class CustomBottomNavigation extends StatelessWidget {
const CustomBottomNavigation({this.selectedIndex = 0, this.onTap});
final int selectedIndex;
final void Function(int) onTap;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: onTap,
);
}
}
This way your code will be refactored into two simple widgets.