Flutter send and show data between BottomNavigationBarItem - flutter

I want to share and show data between BottomNavigationBarItems
It is my BottomNavigationBar page:
class BottomNavigationBarController extends StatefulWidget {
#override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> {
List<Widget> pages = [];
#override
void initState() {
pages.add(HomePage(
key: PageStorageKey('HomePage'),
));
pages.add(MapPage(
key: PageStorageKey('MapPage'),
));
super.initState();
}
int _currentIndex = 0;
Widget _bottomNavigationBar(int selectedIndex) => BottomNavigationBar(
onTap: (int index) => setState(() => _currentIndex = index),
currentIndex: selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.map), label: "MapPage"),
],
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("iArrived"),
actions: _currentIndex != 1
? <Widget>[
IconButton(
icon: Icon(Icons.keyboard_arrow_right),
onPressed: () {
setState(() {
_currentIndex++;
});
})
]
: null),
backgroundColor: Colors.white,
bottomNavigationBar: _bottomNavigationBar(_currentIndex),
body: IndexedStack(
index: _currentIndex,
children: pages,
),
);
}
}
And I want to send my location from the MapPage to the HomePage.
There is a function in the MapPage that is executed when the place change:
void _setLocation(LatLng latlng) {
setState(() {
lat = latlng.latitude;
lng = latlng.longitude;
});
}
I think I have to call some function like a callback inside _setLocation() but I don't know how to implement it to show the location on my HomePage and have it refresh every time I change it.
Thanks for the help.
If your answer is to use shared_preferences plugin, can you tell me how would you implement it? Because I tried it and it did not work for me.
Thanks again.

You can use provider plugin for state management:
Create a model that store a shared value:
class LocationModel extends ChangeNotifier {
Location location;
void updateLocation(Location location) {
this.location = location;
notifyListeners();
}
}
Wrap your root widget with Provider class:
#override
Widget build(BuildContext build) {
return ChangeNotifierProvider(
create: (_) => LocationModel(),
child: App(),
);
}
When you changing a shared value in a model (you need to get a model from Provider, it depends on concrete implementation). For example:
onTap() {
model.updateLocation(newLocation):
}

Related

Loading data with Provider: Flutter

I am working on a Flutter App in which I want to load data using a REST API.
The provider class is as follows-
CategoryProvider-
class CategoryProvider with ChangeNotifier {
SharedPreferences? sharedPreferences;
late LoadingStatus loadingStatus;
late CategoryService _categoryService;
List<Category>? allCategories;
CategoryProvider.initialze(SharedPreferences sf) {
_initializePrefs(sf);
loadingStatus = LoadingStatus.NOT_STARTED;
_categoryService = CategoryService();
}
void _initializePrefs(SharedPreferences sf) {
log('Initializing sharedPreferences');
sharedPreferences = sf;
log('Shared preference initialized');
}
void fetchAllCategories() async {
allCategories = [];
loadingStatus = LoadingStatus.LOADING;
Map<String, dynamic> result = await _categoryService.fetchAllCategories(
token: sharedPreferences!.getString(BEARER_TOKEN) ?? 'null');
if (result['code'] == '2000') {
allCategories = categoryFromJson(result['data']);
} else {
log('No categories: code: $result');
allCategories = [];
}
loadingStatus = LoadingStatus.COMPLETED;
notifyListeners();
}
}
UI Code-
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Widget> _pages = [
PersonalFeedPage(),
ExplorePage(),
ProfilePage(),
SettingsPage(),
];
late PageController _pageController;
int _selectedIndex = 0;
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
_onTapped(int index) {
setState(() {
_selectedIndex = index;
});
_pageController.jumpToPage(index);
}
void onPageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
final categoryProvider = Provider.of<CategoryProvider>(context);
if (categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED) {
// log('Bottom Navigation: loading user for email: ${userProvider.sharedPreferences!.getString(EMAIL)} ');
log("Bottom Navigation: fetching all categories");
categoryProvider.fetchAllCategories();
}
return Scaffold(
body: (!(categoryProvider.loadingStatus == LoadingStatus.LOADING ||
categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED))
? PageView(
children: _pages,
controller: _pageController,
onPageChanged: onPageChanged,
)
: Center(
child: Container(
height: 100,
width: 100,
child: CustomLoadingIndicator(),
),
),
bottomNavigationBar:
(!(categoryProvider.loadingStatus == LoadingStatus.LOADING ||
categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED))
? BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onTapped,
items: [
const BottomNavigationBarItem(
label: "Feed",
icon: Icon(FontAwesomeIcons.rss),
),
const BottomNavigationBarItem(
label: "Explore",
icon: Icon(FontAwesomeIcons.borderAll),
),
const BottomNavigationBarItem(
label: "Profile",
icon: Icon(FontAwesomeIcons.user),
),
const BottomNavigationBarItem(
label: "Settings",
icon: Icon(FontAwesomeIcons.gears),
),
],
)
: null,
);
}
}
What I want to do-
The HomePage consists of a bottom navigation bar which can be used to navigate between the four pages. Home Page is the first widget to be built when app is opened.
Now, when the app is opened, I want to fetch all the data using the fetchAllCategories() method (which are stored in the allCategories variable).
The same fetchAllCategories() might be called from other parts of app to refresh data.
My approach-
I am using the loadingStatus variable in the CategoryProvider to keep track of the data loaded.
If the data is getting loaded, it will be set as LOADING, else if not started fetching then as NOT_STARTED else if loaded then COMPLETED.
The widgets will get built accordingly.
My Question-
I am fetching data in the build() method of the Home Page because I can't access context outside it. So, will this approach be efficient in loading data or is there some more efficient approach for this functionality? Although, this would work but I am not sure this is efficient and correct approach when I have to re-fetch the data?
With ObjectBox instead of SharedPreferences you could read the data in sync. This would make the code a bit cleaner. I am using BLoC and with this package I would load the data in a quite different pattern. However, with ObjectBox you might be able to streamline your code such that it doesn't matter.

How to call init method or specific function again when we click on already activated bottom menu

I have implemented following BottomNavigation
class AppMenu extends StatefulWidget {
const AppMenu({Key? key}) : super(key: key);
#override
State<AppMenu> createState() => _AppMenuState();
}
class _AppMenuState extends State<AppMenu> {
int current = 0;
final List<String> titles = [
"Home 1",
"Home 2"
];
final List<Widget> views = [
const HomeView1(),
const HomeView2(),
];
final List<String> icons = [
"icon_1",
"icon_2",
];
final List<String> barTitles = ["Home1", "Home2"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HomeAppBar(
title: titles[current],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
current = index;
});
},
selectedItemColor: const Color(0xff6B6B6B),
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedItemColor: const Color(0xff6B6B6B),
selectedLabelStyle: const TextStyle(fontSize: 12),
unselectedLabelStyle: const TextStyle(fontSize: 12),
items: views.map((e) {
final itemIndex = views.indexOf(e);
return BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Image.asset(
"assets/images/${icons[itemIndex]}${itemIndex == current ? "" : "_disabled"}.png",
width: 25,
),
),
label: barTitles[itemIndex],
);
}).toList()),
body: Column(
children: [
Expanded(child: views[current]),
],
),
);
}
}
Now it works perfect when I click on home1 and home2 bottom menu and it shows respected widget and load all the content which I have wrote on initState of home1 and home2 but now assume that I am on home1 and if I click again home1 then it is not calling initState again.
I want to call initState or specific function if user click on that menu even if it is selected.
Is there any way to do it?
You can create a initialize or initXXX function to initialize something in initState or somewhere. If parent widget call setState(), then child widget will call didUpdateWidget().
void initialize() {
// do something
}
Call initialize() in initState().
void initState() {
super.initState();
initialize();
}
Call initialize() in didUpdateWidget() of page(child widget).
#override
void didUpdateWidget(covariant PageTest oldWidget) {
super.didUpdateWidget(oldWidget);
initialize();
}
To handle the case in a simple way. You can add your method in onTap of BottomNavigationBar and then pass your data down to the widget tree.
It's only a demonstration to handle your case, you can adjust it with your own liking
For example
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
if(current == index){
foo = yourMethodHere();
}
setState(() {
current = index;
});
},
Pass the variable in the tree
List<Widget> get views => [
HomeView1(foo),
HomeView2(foo),
];

Flutter - I can't change the state of a static method of onWillPop

I have a bottomNavigatorBar in my app, and on some screens I don't make it visible, so create a static method to handle it from any class. it works, but when I use the instance of this static method in onWillPop I have the following problems...
E/flutter (10927): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: setState() called in constructor: _telaPrincipalState#cb9c4(lifecycle state: created, no widget, not mounted)
E/flutter (10927): This happens when you call setState() on a State object for a widget that hasn't been inserted into the widget tree yet. It is not necessary to call setState() in the constructor, since the state is already assumed to be dirty when it is initially created
I already used mounted, but it still didn't work
if(!this.mounted){
}
Here is the 3 page code where I use routes
class AtivarEmailPage extends StatefulWidget {
static const String route = "/ativarEmail";
#override
_AtivarEmailPageState createState() => _AtivarEmailPageState();
}
class _AtivarEmailPageState extends State<AtivarEmailPage> {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
if (!mounted) {
setState(() => telaPrincipal.show());
}
Navigator.of(context).pushNamedAndRemoveUntil(
HomeView.route, (Route<dynamic> route) => false,
arguments: null);
},
child: Scaffold(code...),);
}
}
the problem happens when I call onWillpop to return to HomeView
class HomeView extends StatefulWidget {
static const String route = "/";
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
Widget home(BuildContext context) {
return Scaffold(code..);
}
}
the bottomNavigatorBar manipulated by the static method is in the code below. It is the screen that is under the whole stack, so the navigation bar is floating on top of the other pages
class telaPrincipal extends StatefulWidget {
telaPrincipal({this.categoria, this.exercicio});
static _telaPrincipalState tela = _telaPrincipalState();
#override
_telaPrincipalState createState() {
return tela;
}
static indexBar(int index) {
tela.onItemTapped(index);
}
static void hide() {
tela.hideNavBar();
}
static void show() {
print("show");
tela.showNavBar();
}
}
class _telaPrincipalState extends State<telaPrincipal> {
void hideNavBar() {
setState(() {
_show = false;
_bottomBarHeight = 0;
});
}
void showNavBar() {
setState(() {
_show = true;
_bottomBarHeight = 60;
});
}
void onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
int _selectedIndex = 0;
final navigatorKey = GlobalKey<NavigatorState>();
//bottomNavigatorBar routes
final pagesRouteFactories = {
HomeView.route: () => MaterialPageRoute(builder: (context) => HomeView()),
programaTreino.route: () => MaterialPageRoute(
builder: (context) => programaTreino(
exercicioEscolhido: null,
categoria: null,
)),
pesquisar_view.route: () =>
MaterialPageRoute(builder: (context) => pesquisar_view()),
};
bool _show = true;
double _bottomBarHeight = 60;
#override
Widget build(BuildContext context) {
return WillPopScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: HexColor("#121212"),
body: _buildBody(),
bottomNavigationBar: Container(
height: _bottomBarHeight,
width: MediaQuery.of(context).size.width,
child: _show
? _buildBottomNavigationBar(context)
: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
)),
),
onWillPop: () async {
navigatorKey.currentState.maybePop();
return false;
},
);
}
Widget _buildBody() => MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: navigatorKey,
onGenerateRoute: (settings) {
String routeName = settings.name;
//Map<String, dynamic> args = route.arguments; // Get any arguments passed to the route
print("ongenate$routeName}");
switch (routeName) {
case AtivarEmailPage.route:
return MaterialPageRoute(builder: (context) => AtivarEmailPage());
break;
default:
return pagesRouteFactories[settings.name]();
break;
}
});
Widget _buildBottomNavigationBar(context) => BottomNavigationBar(
currentIndex: _selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_filled),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.fitness_center),
label: 'Treinos',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Pesquisar',
),
],
onTap: (routeName) {
print(pagesRouteFactories.keys.toList()[routeName]);
navigatorKey.currentState.pushNamed(
pagesRouteFactories.keys.toList()[routeName],
arguments: routeName == 0 ? null : [null, null]);
onItemTapped(routeName);
},
backgroundColor: HexColor("#FFFFFF").withOpacity(0.08),
selectedItemColor: HexColor("#FFCC80"),
unselectedItemColor: HexColor("#FFFFFF").withOpacity(0.30),
);
}
screens:
HomeView
AtivarEmail
I would like to go back with the bottomNavigatorBar visible to Homeview after pressing the android back button on the ActivateEmail screen
so create a static method to handle it from any class.
The short answer is: don't do that.
Read up on flutter state management. Pick the one you like best, but don't home-brew a solution.
It looks like you are using mounted in reverse. mounted is true when the widget is part of the tree. See The API documentation
Try
if (mounted) {
setState(() => telaPrincipal.show());
}
instead of if (!mounted)

send data from widget to another widget in flutter

I want to send data from widget to another widget, in my example i want to send some filter data from FilterScreen.dart to ShopScreen.dart
it works fine but i dont know is what i'm doing is correct?
in filter model file:
class FilterData with ChangeNotifier {
bool isFreeShipping;
bool isSomeThingElse;
FilterData({this.isFreeShipping = false, this.isSomeThingElse = false});
void setFreeShippingValue(bool newval) {
isFreeShipping = newval;
notifyListeners();
}
void setSomeThingElseValue(bool newval) {
isSomeThingElse = newval;
notifyListeners();
}
}
in main.dart:
return ChangeNotifierProvider(
create: (context) => FilterData(),
child: MaterialApp(
.........
)
);
in tabs screen:
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
List<Map<String, Object>> _pages;
int _selectedPageIndex = 0;
#override
void initState() {
_pages = [
{
'page': ShopScreen(),
'title': 'shop',
},
{
'page': FilterScreen(),
'title': 'filter',
},
];
super.initState();
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
drawer: DrawerApp(),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.shop),
title: Text('Shop'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.search),
title: Text('Filter'),
),
],
),
);
}
}
in FilterScreen.dart:
class FilterScreen extends StatefulWidget {
#override
_FilterScreenState createState() => _FilterScreenState();
}
class _FilterScreenState extends State<FilterScreen> {
#override
Widget build(BuildContext context) {
final data = Provider.of<FilterData>(context);
return Container(
child: Center(
child: Expanded(
child: ListView(
children: <Widget>[
SwitchListTile(
title: Text('Free Shipping'),
value: data.isFreeShipping,
subtitle: Text('get free shipping products'),
onChanged: (newValue) {
data.setFreeShippingValue(newValue);
}),
SwitchListTile(
title: Text('Some thing else'),
value: data.isSomeThingElse,
subtitle: Text('get filtred products'),
onChanged: (newValue) {
data.setSomeThingElseValue(newValue);
}),
],
),
),
),
);
}
}
in ShopScreen.dart:
class ShopScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final data = Provider.of<FilterData>(context);
return Container(
child: Center(
child: Text(
data.isFreeShipping ? 'get favorite Products' : 'get all products'),
),
);
}
}
enter image description here
Your question indeed is a pain for most of the developers, which is like I don't know how it works!
So, if you are not able to understand. there are two reasons to that:
You just blindly followed the tutorial or documentation, cos of the time constraints
You did not understand how Flutter Provider State Management works. So, for that, do read upon these:
List of state managements in flutter
Flutter provider package, of course you have used that in your project. But read how he is using.
So, now let us jump to the code. How your code works?
There are multiple things which are responsible for this:
1. Provider Wrap: If you closely look into the main.dart code, you have done this
return ChangeNotifierProvider(
create: (context) => FilterData(), // here you define the ChangeNotifier class
child: MaterialApp(
.........
)
);
Now looking at the above code, you see, whenever you wrap the app with the ChangeNotifierProvider(), it always rebuilds whenever there is a state change in the class which you have provided inside that, in this case FilterData(). Any changes happens will reflect in the whole app, cos, ChangeNotifierProvider(), is keep rebuilding the state of the immediate child, in this case your, MaterialApp(), which is wrapped.
2. NotifyChanges from the ChangeNotifier class: If you look at your FilterData, it is the one which is responsible for the rebuilding of the app, which is wrapped by the ChangeNotifierProvider().
Let us see how:
void setFreeShippingValue(bool newval) {
isFreeShipping = newval;
notifyListeners();
}
void setSomeThingElseValue(bool newval) {
isSomeThingElse = newval;
notifyListeners();
}
If you closely take a look at the methods, which I mentioned in the above code from your FilterData class only, they have notifyListeners(). These are the ones, which is responsible, whenever your two methods called, it notifies the ChangeNotifierListener to rebuild the widget, and hence you see the updated data every time, you use any of the two methods
3. Using NotifyListeneres method from the FilterData in FilterScreen: So, again if we look closely at the thing which we have mentioned in the point 2, we see that, the method method should be called to make changes in the App which is the immediate child of ChangeNotifierProvider()
SwitchListTile(
title: Text('Free Shipping'),
value: data.isFreeShipping,
subtitle: Text('get free shipping products'),
onChanged: (newValue) {
data.setFreeShippingValue(newValue);
}),
SwitchListTile(
title: Text('Some thing else'),
value: data.isSomeThingElse,
subtitle: Text('get filtred products'),
onChanged: (newValue) {
data.setSomeThingElseValue(newValue);
}),
So, when you call any of the methods in your onChanged, it straight away notifies the Provider that, the value has been changed, and the app rebuilds, and when you switch to the other tab, you see updated result like magic.
MOST IMPORTANT: Your final data = Provider.of<FilterData>(context);, is an instance of the Provider class, which trigger the method to help notify the ChangeNotifierProvider() to make changes in the app
So the mapping is like that:
Listens to the change
FilterData {setFreeShippingValue, setSomeThingElseValue} <----------------------> ChangeNotifierProvider() REBUILDS MATERIALAPP()

I want to check if any Widget exist in a List?

I want to load pages from a List and when the user taps on an item from the drawer he can go to that page (if it's already opened) otherwise the Widget will load in the selected page.
But I can't find if that widget is already exists in the List if(myList.contains(Widget1())) => print('it exist'); One guy told me to override hashCode and operator==
class Widget6 extends StatelessWidget {
final String title = 'Widget6';
final Icon icon = Icon(Icons.assessment);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
#override
bool operator ==(dynamic other) {
final Widget6 typedOther = other;
return title == typedOther.title && icon == typedOther.icon;
}
#override
int get hashCode => hashValues(title, icon);
}
if I do that I can't use any child widget to those widgets. Getting exception like: type 'Center' is not a subtype of type 'Widget6'. I copied this from flutter gallery I didn't find good documentation/guide. Sorry, I am a beginner.
Complete code below
class _MyHomePageState extends State<MyHomePage> {
List pageList = [
Widget1(),
Widget2(),
Widget3(),
Widget4(),
];
PageController _pageController;
int _selectedIndex = 0;
#override
void initState() {
_pageController = PageController(
initialPage: _selectedIndex,
);
super.initState();
}
void navigatePage(Widget widget) {
// problem is here
if (pageList.contains(widget)) {
_pageController.animateToPage(pageList.indexOf(widget, 0),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
else {
setState(() {
pageList.removeAt(_pageController.page.toInt());
pageList.insert(_pageController.page.toInt(), widget);
});
_pageController.animateToPage(_pageController.page.toInt(),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
Widget1(),
),
),
ListTile(
title: Text('Widget2'),
onTap: () => navigatePage(
Widget2(),
),
),
ListTile(
title: Text('Widget3'),
onTap: () => navigatePage(
Widget3(),
),
),
ListTile(
title: Text('Widget4'),
onTap: () => navigatePage(
Widget4(),
),
),
ListTile(
title: Text('Widget5'),
onTap: () => navigatePage(
Widget5(),
),
),
ListTile(
title: Text('Widget6'),
onTap: () => navigatePage(
Widget6(),
),
),
],
),
),
appBar: AppBar(
title: Text(widget.title),
),
body: PageView.builder(
onPageChanged: (newPage) {
setState(() {
this._selectedIndex = newPage;
});
},
controller: _pageController,
itemBuilder: (context, index) {
return Container(
child: pageList[index],
);
},
itemCount: pageList.length,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) => setState(() {
_selectedIndex = index;
_pageController.animateToPage(index,
duration: Duration(milliseconds: 300), curve: Curves.ease);
}),
items: pageList.map((page) {
return BottomNavigationBarItem(
backgroundColor: Colors.deepOrangeAccent,
icon: page.icon,
title: Text(page.title));
}).toList(),
),
);
}
}
Here List of dummy Widgets
class Widget1 extends StatelessWidget {
final String title = 'Widget1';
final Icon icon = Icon(Icons.school);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
}
class Widget2 extends StatelessWidget {
// only title and icon are changed
}
class Widget3 extends StatelessWidget {
// only title and icon are changed
}
class Widget4 extends StatelessWidget {
// only title and icon are changed
}
class Widget5 extends StatelessWidget {
// only title and icon are changed
}
class Widget6 extends StatelessWidget {
// only title and icon are changed
}
Okay, I found the solution. And it has to do with operator== overriding
I missed this line if (runtimeType != other.runtimeType) return false;
The whole code stays the same.
#override
// ignore: hash_and_equals
bool operator ==(dynamic other) {
if (runtimeType != other.runtimeType) return false;
final Widget6 typedOther = other;
return title == typedOther.title;
}
#Ahmed Sorry for the late reply, I decided to put it in an answer rather than a comment.
One solution is yours, overriding == but I was thinking of using Key and then instead of using contains method, using something like:
if(myList.indexWhere((Widget widget)=> widget.key==_key) != -1)...
Suggestion
You can store icon and title as a map or a module instead of making 6 different Widget.
You can create another file, saying module.dart like this:
class Module {
final String title;
final Icon icon;
Module(this.title, this.icon);
#override
int get hashCode => hashValues(title.hashCode, icon.hashCode);
#override
bool operator ==(other) {
if (!identical(this, other)) {
return false;
}
return other is Module &&
this.title.compareTo(other.title) == 0 &&
this.icon == other.icon;
}
}
Then create another file that builds the page, saying mywidget.dart, like this:
class MyWidget extends StatelessWidget {
final Module module;
MyWidget({Key key,#required this.module}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: module.icon,
);
}
}
Then on each ListTile's onTap, Navigate like this:
...
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
MyWidget(module: Module('Widget1', Icon(Icons.school)),)
),
),
...
So instead of storing Widgets, you store a Type(Here Module) that you declared.
You can also use the list's map to build each ListTile of the ListView for each Module, instead of doing it one by one. (if each item on the drawer are similar), Something like this:
List<Module> myTabs = [
Module('Widget1', Icon(Icons.school)),
Module('Widget2', Icon(Icons.home)),
];
...
Drawer(
child: ListView(
children:myTabs.map((Module module)=> ListTile(
title:Text( module.title),
onTap: navigatePage(MyWidget(module: module,)),
)).toList(),
) ,
);
...