I am using a bottom navigation bar witch display a class according to the current index,
I used a ternary condition to check witch class to display on the first index using this code :
class Home extends StatefulWidget {
static String homeindex = "main";
#override
_HomeState createState() => _HomeState();
}
final greencol = HexColor("#2f7e7b");
class _HomeState extends State<Home> {
static int _currentindex = 0;
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
final tabs = [
(Home.homeindex == 'main') ? Homehome() : Green(),
Assistant(),
Profile(),
];
bottomNavigationBar: Container(
height: 55,
child: BottomNavigationBar(
currentIndex: _currentindex,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
selectedItemColor: greencol,
unselectedItemColor: Colors.grey,
selectedLabelStyle:
GoogleFonts.roboto(fontWeight: FontWeight.bold),
iconSize: 25,
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.home_outlined,
),
title: Text(
'Home',
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.mic_outlined,
),
title: Text(
'Assistance',
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.person_outlined,
),
title: Text(
'Profile',
),
),
],
onTap: (index) {
setState(() {
_currentindex = index;
});
},
),
)),
);
}
}
using a static string called home index I'm trying to change the display widget on the first index using set state from another class :
class Homehome extends StatefulWidget {
const Homehome({Key? key}) : super(key: key);
#override
_HomehomeState createState() => _HomehomeState();
}
class _HomehomeState extends State<Homehome> {
.....
child: ButtonTheme(
minWidth: 20.0,
height: 30.0,
child: RaisedButton(
onPressed: () {
setState(() {
Home.homeindex = 'greenhouse';
print(Home.homeindex);
});
},
but it's only setting state to the current class widget.
what's the easiest way to rebuild the widget from the last page ?
You need to bring your void callback from Homehome one level up to Home, then call setState to update the Home widget.
class Homehome extends StatefulWidget {
const Homehome({Key? key, required this.onPressedMyButton }) : super(key: key);
final VoidCallBack onPressedMyButton;
.....
child: ButtonTheme(
minWidth: 20.0,
height: 30.0,
child: RaisedButton(
onPressed: widget.onPressedMyButton,
In your Home widget:
.....
final tabs = [
(Home.homeindex == 'main') ? Homehome(onPressedMyButton: () {
setState(() {
Home.homeindex = 'greenhouse';
print(Home.homeindex);
});
}, ) : Green(),
Assistant(),
Profile(),
];
.....
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 .
So I have a Row and two containers; first for selection of page and second for navigating to that selected page. I pass a string value to the 2nd container for identification which page should I navigate towards.
use NavigationRail
example:
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 = '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;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
NavigationRail(
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
labelType: NavigationRailLabelType.selected,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.favorite_border),
selectedIcon: Icon(Icons.favorite),
label: Text('First'),
),
NavigationRailDestination(
icon: Icon(Icons.bookmark_border),
selectedIcon: Icon(Icons.book),
label: Text('Second'),
),
NavigationRailDestination(
icon: Icon(Icons.star_border),
selectedIcon: Icon(Icons.star),
label: Text('Third'),
),
],
),
const VerticalDivider(thickness: 1, width: 1),
// This is the main content.
Expanded(
child: Center(
child: Text('selectedIndex: $_selectedIndex'),
),
)
],
),
);
}
}
here is the simple solution, you can add page or widget to list then add setstate and index of widget to button onPressed function. when you pressed to button its gonna show the page
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
final screen = [
const Center(child: Text('Home')),
const Center(child: Text('Search')),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Container(
width: 200,
color: Colors.red,
child: Column(
children: [
IconButton(
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: Icon(Icons.home)),
IconButton(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: Icon(Icons.search)),
],
),
),
screen[_selectedIndex]
],
),
);
}
}
I want to get data from Provider based on argument and populate that data into each BottomNavigationBarItem
On this screen I get "meetingId" argument and use that on Provider to populate a model class with data.
class MeetingDetailScreen extends StatefulWidget {
final meetingId;
const MeetingDetailScreen({Key? key, this.meetingId}) : super(key: key);
static const routeName = '/meeting-detail';
#override
_MeetingDetailScreenState createState() => _MeetingDetailScreenState();
}
class _MeetingDetailScreenState extends State<MeetingDetailScreen> {
String meetingId = '';
var _isInit = true;
var _isLoading = false;
final List<Widget> _pages = [
DetailsScreen(),
DocumentsScreen(),
AgendaScreen(),
MinutesScreen(),
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
//This is provider
Provider.of<MeetingDetails>(context)
.fetchAndSetMeetingDetails(widget.meetingId)
.then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 248, 1),
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
iconTheme: const IconThemeData(color: Color.fromRGBO(150, 188, 51, 1)),
title: Image.asset(
"assets/images/appbar-logo.png",
fit: BoxFit.contain,
height: 50,
),
),
drawer: const MainDrawer(),
body: _pages[_selectedPageIndex],
//body: getPage(_selectedPageIndex),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.info_outline),
label: 'Details',
),
BottomNavigationBarItem(
icon: Icon(Icons.file_download),
label: 'Documents',
),
BottomNavigationBarItem(
icon: Icon(Icons.format_list_bulleted_outlined),
label: 'Agenda',
),
BottomNavigationBarItem(
icon: Icon(Icons.note_alt_outlined),
label: 'Minutes',
),
],
currentIndex: _selectedPageIndex,
selectedItemColor: Colors.amber[800],
onTap: _selectPage,
),
);
}
}
This is the BottomNavigationBarItem screen "DetailsScreen()"
class DetailsScreen extends StatelessWidget {
static const routeName = '/meeting-detail';
const DetailsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final meetingDetailsList = Provider.of<MeetingDetails>(context);
final meetingDetails = meetingDetailsList.items.toList()[0];
return Center(
child: Text(
meetingDetails.event_name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
);
}
}
Problem is Provider updates data from API and it takes a moment. when I try to use Provider data on BottomNavigationBar it throws an error because provider data is null when loading the screen. Is there any way to delay the BottomNavigationBarItem until Provider populates data?
Thank you
Problem is I wasn't using builder with ChangeNotifierProvider.
Inside widget I used following builder code and problem solved.
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: meeting.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: meeting[i],
child:
buildDetailRow(context, 'Meeting Name', meeting[i].event_name),
),
),
],
);
I am new at flutter, and I am trying to change the color of a button when the button is active (been pressed), my code is not working as per expectation. Someone knows how to I fix that?
My code:
import 'package:flutter/material.dart';
class BottomNavBar extends StatefulWidget {
const BottomNavBar({Key? key}) : super(key: key);
#override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
iconButtonBar(context, Icons.home, 0, _selectedIndex),
iconButtonBar(context, Icons.favorite, 1, _selectedIndex),
iconButtonBar(context, Icons.person, 2, _selectedIndex),
iconButtonBar(context, Icons.search, 3, _selectedIndex),
],
),
);
}
Container iconButtonBar(
BuildContext context, IconData icon, int index, int _selectedIndex) {
return Container(
height: 60,
width: MediaQuery.of(context).size.width / 4,
color: index == _selectedIndex ? Colors.blue : Colors.white, // changing the color
child: IconButton(
icon: Icon(icon),
onPressed: () {
_selectedIndex = index;
},
));
}
}
really happy if you find the time to answer.
You should try to refer below code:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
PageController _pageController = PageController();
List<Widget> _screen = [
Home(),
MyProfile(),
Conversations(),
SearchPage()
];
void _onPageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
void _onItemTapped(int selectedIndex) {
_pageController.jumpToPage(selectedIndex);
}
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: _screen,
onPageChanged: _onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: this._selectedIndex,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.black45,
backgroundColor: Colors.black,
selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),
onTap: _onItemTapped,
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
label: 'Profile'),
BottomNavigationBarItem(
icon: Icon(
Icons.sms,
),
label: 'Messages'),
BottomNavigationBarItem(
icon: Icon(
Icons.search,
),
label: 'Search'),
],
),
);
}
}
You need to call setState(); so that the changes will be reflected to the UI. so your code looks
Container iconButtonBar(
BuildContext context, IconData icon, int index, int _selectedIndex) {
return Container(
height: 60,
width: MediaQuery.of(context).size.width / 4,
color: index == _selectedIndex ? Colors.blue : Colors.white, // changing the color
child: IconButton(
icon: Icon(icon),
onPressed: () {
setState((){
_selectedIndex = index;
});
},
));
}
I am using an Animated Container in Bottom Nav Bar to Hide the Bottom Navbar While Scrolling(Similar to Pinterest). I want to display The data of particular tab in the Same Page. (refer the GIF)
https://i.stack.imgur.com/xlOsy.gif
I Want to display the Content In the Screen of the tab and I don't want to route to other Screen leaving the main Screen. I have 4 rows with different Icons. How Can I achieve this?
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
double yTransValue = 0;
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollUpdateNotification>(
onNotification: (notification) {
print(notification.metrics.axisDirection);
print(notification.metrics.axis);
if (notification.scrollDelta.sign == 1) {
setState(() {
yTransValue = 100;
});
} else if (notification.scrollDelta.sign == -1) {
setState(() {
yTransValue = 0;
});
}
},
child: Scaffold(
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 260,
child: Card(
child: Center(child: Text('Item #$index')),
),
);
},
),
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 300),
transform: Matrix4.translationValues(0, yTransValue, 0),
child: SizedBox(
height: 60,
child: Card(
color: Colors.grey,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.home),
Icon(Icons.search),
Icon(Icons.favorite_border),
Icon(Icons.person_rounded),
],
),
),
),
),
),
);
}
}
I had tried Wrapping the Icons With 'Gesture Detector' But when the Icon is pressed a data is loaded in new Screen.
Use the following code:
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,
),
);
}
}