Flutter how to remove open routes when changing page inside IndexedStack - flutter

In my app I have a NavigationRail on the left and the main body on the right. I'm using nested Navigators so that when a widget inside the IndexedStack pushes a new route the NavigationRail stays on the screen. But this generates a problem when there is a new Navigator Route on the screen and I try to change the page with NavigationRail it changes the selected icon, but the Navigator route stays on the screen.
Row(
children: [
// GestureDetector uses onHorizontalDragStart to make the Navigation
// Rail extend or shrink
GestureDetector(
onHorizontalDragStart: (details) {
setState(() {
_navRailExtended = !_navRailExtended;
});
},
child: NavigationRail(
// The width of the navigation rail
minExtendedWidth: 180,
// Text and icon themes for selected destinations
selectedIconTheme:
IconThemeData(color: Colors.purple[600]),
selectedLabelTextStyle: TextStyle(
color: Colors.purple[600],
fontWeight: FontWeight.bold,
fontSize: 16),
// Text and icon themes for unselected destinations
unselectedIconTheme:
IconThemeData(color: Colors.grey[200]),
unselectedLabelTextStyle: TextStyle(
color: Colors.grey[400],
fontWeight: FontWeight.bold,
fontSize: 15),
// Background color of the navigation rail, typical
// color for the dark theme in Flutter
backgroundColor: const Color.fromARGB(255, 47, 47, 47),
// extended makes the navigation rail wider
extended: _navRailExtended,
selectedIndex: _pageIndex,
onDestinationSelected: (int index) {
setState(() {
_pageIndex = index;
});
},
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.explore_rounded, size: 35),
label: Text('Discover'),
),
NavigationRailDestination(
icon: Icon(Icons.home_filled, size: 35),
label: Text('Notebook'),
),
NavigationRailDestination(
icon: Icon(Icons.games_sharp, size: 35),
label: Text('Games'),
),
NavigationRailDestination(
icon: Icon(Icons.analytics_outlined, size: 35),
label: Text('Analytics'),
),
],
),
),
// A vertical divider between the navigation rail and the body
const VerticalDivider(thickness: 1.5, width: 1),
// Expanded makes the body of the windows app centered
// (without it the row widget would screw up the sizing of the app)
Expanded(
// Navigator makes all new routes inside the main pages
// opened with 'Navigator.pop()' ignore the navigation rail,
// i.e. the navigation rail will always stay opened
child: Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(builder: ((context) {
// Indexed Stack keeps the state of each page unchanged
return IndexedStack(
index: _pageIndex,
children: const [Discover(), NoteBook(), Games(), Analytics()],
);
}));
},
),
)
],
)
App on the start:
When there is a new Navigator route on the screen:
I try to change the page:
What it should do:

Related

Flutter: Multiple Tap Gestures for Text

I'm looking for a way to add multiple tap gestures to a block of text.
The block of text may consist of a hyperlink. The goal is that if a user taps on the link, navigate to that web page. If they tap anywhere else on the block of text, navigate to a detail view.
I am using Linkable to detect the links. That portion works as expected, but the other tap does not.
Here is what I have tried:
return InkWell(
onTap: () {
AutoRouter.of(context).push(
// Detailed Page
);
}
},
child: Container(
padding: const EdgeInsets.all(16.0),
child: Linkable(
text: myText,
textColor: Colors.black,
linkColor: Colors.blue,
),
),
);
You can use the RichText widget to break your texts into TextSpans. Add the following code to the TextSpan you want to make clickable.
TextSpan(
text: "Clickable text.",
style: TextStyle(
color: Colors.blue,
),
recognizer: TapGestureRecognizer()
..onTap = () {
doStuff();
},
),

Flutter - How to position snackbar above system navigation bar

I have a transparent system navigation bar in my application.
Getx snack bar is used by the app to throw errors and success messages. What I'm stuck with is that anytime the app throws a snack bar, the snack bar appears and disappears through the transparent navigation bar (i can see the snack bar move through the navigation bar). Is there any way to have the snack bar appear above the navigation bar? How can I position the snack bar above the system navigation bar?
You may want to look into SnackBarBehavior enum which allows one to change the position. If you are using a third party library then you may want to try putting the behavior into an extension method. Alternative ideas exist on this SO Question that may apply in your use case although not an exact duplicate.
The SnackbarBehavior enum states:
Defines where a SnackBar should appear within a Scaffold and how its location should be adjusted when the scaffold also includes a FloatingActionButton or a BottomNavigationBar.
Other ideas include using a position widget with a Stack widget to gain precise control. Add a MediaQuery or other fractional widgets to allow for multiple screen sizes.
I suggest you to use SnackBar Widget that developed by flutter team instead of Getx snackbar. It basically appears above of navigation bar here an basic example;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: ElevatedButton(
child: const Text('Show Snackbar'),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Awesome Snackbar!'),
action: SnackBarAction(
label: 'Action',
onPressed: () {
// Code to execute.
},
),
),
);
},
),
),
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,
),
);
}
static GetSnackBar SuccessSnackBar({String title = 'Success', String message}) {
Get.log("[$title] $message");
return GetSnackBar(
titleText: Text(title.tr, style: Get.textTheme.headline6.merge(TextStyle(color: Get.theme.primaryColor))),
messageText: Text(message, style: Get.textTheme.caption.merge(TextStyle(color: Get.theme.primaryColor))),
snackPosition: SnackPosition.BOTTOM,
margin: EdgeInsets.all(20),
backgroundColor: Colors.green,
icon: Icon(Icons.check_circle_outline, size: 32, color: Get.theme.primaryColor),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
borderRadius: 8,
dismissDirection: DismissDirection.horizontal,
duration: Duration(seconds: 5),
);
}
Used like this in my app. Worked fine.
Get.snackbar(
"Bruh",
"Your quantity have to at least one",
colorText: Colors.black,
backgroundColor: Palette.yellowColor,
);
In my case, this is my return to show snackbar for error message, the snackbar appear on top below the appbar
In the getx snackbar there are a snackPosition: attribute
You can use that to display yours

BottomNavigationBar // Cannot control item label color

Goal: I want to give the item label a specific font and color depending on if it is selected or not.
My approach: As the label cannot be styled directly, I'm using the properties "unselectedLabelStyle" and "selectedLabelStyle".
The Problem:
The properties work for font and fontweight, but I cannot directly control the text color
I can influence the label color of the selected item; but not with the "selectedLabelStyle" property, but with the color I specifiy under "seltectedItemColor".
"unselectedLabelStyle" also works for font and fontweight, but not for the color. I cannot find a property, that would allow me to change the color. > THIS IS THE PROBLEM
Pretty picture (code below):
The code:
BottomNavigationBar(
elevation: 0,
onTap: (index) => selectPage(index),
currentIndex: selectedPageIndex,
selectedItemColor:
Provider.of<CustomColors>(context).customColorScheme['Dark Teal'],
unselectedLabelStyle:
Provider.of<CustomTextStyle>(context, listen: false)
.customTextStyle('IconLabel'),
selectedLabelStyle:
Provider.of<CustomTextStyle>(context, listen: false)
.customTextStyle('IconLabel'),
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
items: [
//home
bottomNavIcon(
context: context,
icon: Image.asset(
"assets/icons/icon_home.png",
),
icon_active: Image.asset("assets/icons/icon_home.png",
color: Provider.of<CustomColors>(context)
.customColorScheme['Dark Teal']),
label: 'Home',
),
//favorite
bottomNavIcon(
context: context,
icon: Image.asset("assets/icons/icon_favorite.png"),
icon_active: Image.asset("assets/icons/icon_favorite.png",
color: Provider.of<CustomColors>(context)
.customColorScheme['Dark Teal']),
label: 'Favorite',
),
//loockback
bottomNavIcon(
context: context,
icon: Image.asset("assets/icons/icon_lookback.png"),
icon_active: Image.asset("assets/icons/icon_lookback.png",
color: Provider.of<CustomColors>(context)
.customColorScheme['Dark Teal']),
label: 'Lookback',
),
//info & support
bottomNavIcon(
context: context,
icon: Image.asset("assets/icons/icon_info.png"),
icon_active: Image.asset("assets/icons/icon_info.png",
color: Provider.of<CustomColors>(context)
.customColorScheme['Dark Teal']),
label: 'Info & Support',
),
],
),
Code for the icons
BottomNavigationBarItem bottomNavIcon(
{required BuildContext context,
required Image icon,
required Image icon_active,
required String label}) {
return BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(top: 6.h, bottom: 3.h),
child: icon,
),
activeIcon: Padding(
padding: EdgeInsets.only(top: 6.h, bottom: 3.h),
child: icon_active,
),
label: label,
);
}
If you want the unselected items to have a certain color, use:
unselectedItemColor: Colors.red,
This will change the color of label and icon both for unselected items.
Example:
Unfortunately, unselectedLabelStyle property works for changing font weight, font size etc but not for color.
Also check this answer for unselectedLabelstyle don't work in Flutter
As the docmumentation say's, you can change the text color using the color property of TextStyle : https://api.flutter.dev/flutter/painting/TextStyle-class.html
Once this say's perharps BottomNavigationBar overide the color when setting selectedItemColor and/or unselectedItemColor
After giving a try, effectively color is overide even if you don't provide selectedItemColor and/or unselectedItemColor

Flutter: Persistent BottomAppBar and BottomNavigationBar

I am new to flutter, and I am building an app that has BottomAppBar which takes the property bottomNavigationBar: of Scaffold() for my home_screen, because I needed it to be at the bottom of the screen and persistent throughout the pages, and I also need a BottomNavigationBar to be persistent also at the top of BottomAppBar, but I can't make that happen because BottomAppBar already takes the bottomNavigationBar: property.
How can I make my BottomNavigationBar persistent alongside my BottomAppBar?
Note: I am using PageView() to scroll through my pages and it will be controlled by the BottomNavigationBar
Edit: attached here is the UI that I am trying to achieve
code snippet:
import 'package:flutter/material.dart';
//screens
import 'package:timewise_flutter/screens/calendar_screen.dart';
import 'package:timewise_flutter/screens/covey_quadrants_screen.dart';
import 'package:timewise_flutter/screens/kanban_screen.dart';
import 'package:timewise_flutter/screens/todo_list_screen.dart';
class OverviewScreen extends StatefulWidget {
static const String id = 'overview_screen';
//const OverviewScreen({Key? key}) : super(key: key);
#override
_OverviewScreenState createState() => _OverviewScreenState();
}
class _OverviewScreenState extends State<OverviewScreen> {
PageController _pageController = PageController(initialPage: 2);
int _bottomNavBarCurrentIndex = 2;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
bottomNavigationBar: SafeArea(
child: BottomAppBar(
elevation: 16.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
tooltip: 'Menu',
icon: Icon(Icons.menu_rounded),
onPressed: () {
print('menu icon pressed!');
//TODO: show bottom modal bottom sheet
},
),
IconButton(
tooltip: 'Pomodoro Timer',
icon: Icon(Icons.hourglass_empty_rounded),
onPressed: () {
print('pomo icon pressed!');
//TODO: show pomodoro timer modal bottom sheet
},
),
IconButton(
tooltip: 'Add',
icon: Icon(Icons.add_circle_outline_outlined),
onPressed: () {
print('add icon pressed!');
//TODO: show add task modal bottom sheet
},
),
],
),
);,
),
body: PageView(
controller: _pageController,
onPageChanged: (page) {
setState(() {
_bottomNavBarCurrentIndex = page;
});
},
children: [
CalendarScreen(),
ToDoListScreen(),
SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Overview Screen'),
BottomNavigationBar(
currentIndex: _bottomNavBarCurrentIndex,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
elevation: 0.0,
iconSize: 16.0,
selectedItemColor: Colors.black,
unselectedItemColor: Colors.grey,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today_rounded),
label: 'Calendar',
tooltip: 'Calendar',
),
BottomNavigationBarItem(
icon: Icon(Icons.checklist_rounded),
label: 'To-Do',
tooltip: 'To-Do List',
),
BottomNavigationBarItem(
icon: Icon(Icons.panorama_fish_eye_rounded),
label: 'Overview',
tooltip: 'Overview',
),
BottomNavigationBarItem(
icon: Icon(Icons.border_all_rounded),
label: 'Covey\'s 4 Quadrants',
tooltip: 'Covey\'s 4 Quadrants',
),
BottomNavigationBarItem(
icon: Icon(Icons.view_column_rounded),
label: 'Kanban Board',
tooltip: 'Kanban Board',
),
],
onTap: (index) {
setState(() {
_bottomNavBarCurrentIndex = index;
_pageController.jumpToPage(index);
});
},
),
],
),
),
),
CoveyQuadrantsScreen(),
KanbanScreen(),
],
),
);
}
}
Unfortunately, this is not a standard way in which the mobile app UI should be designed. This will result in bad user experience.
What if user accidently touches on NavigationBar instead of
AppBar. You will be taken to the new screen and action that I
performed there will be lost or need to handle.
So proper UI guidelines should be met, while we design and develop for the mobile app. Based on guidelines from material.io
Bottom app bars should be used for:
Mobile devices only
Access to a bottom navigation drawer
Screens with two to five actions
Bottom app bars shouldn't be used for:
Apps with a bottom navigation bar
Screens with one or no actions
Refer this link for more useful information about the UI and UX guidelines https://material.io/
I would suggest making a scaffold() with the bottomNavigationBar() as you did. Then you could create a list of Container() objects each representing a different page. For your PageView I'm assuming you have done that, if not then that's the way to do it. Then you could cycle through your pages by setting the body: property of the scaffold to myPages[_currentIndex] or something like that.
Additionally: Like the comment asks, I am also not sure why you would want both BottomNavigationBar and BottomAppBar they both do exactly the same thing. In either case the process is the same as what I described above.

Appbar widgets not refreshing instantly on calling setState() Flutter

The AppBar of my app has the cart widget which uses a badge for showing the number of items in the cart.
When I place the exact same cart in my body of the Scaffold, it updates the state instantly on adding or removing items from the cart. But in my appbar, the cart widget's state doesn't get updated. It's very weird.
actions: <Widget>[
Badge(
badgeContent: Text('${selectedItems.length}',
style: TextStyle(color: Colors.white)),
position: BadgePosition.topRight(top: 0.0, right: 5.0),
badgeColor: Colors.black,
showBadge: selectedItems.length == 0 ? false : true,
child: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () => Navigator.push(context,
CupertinoPageRoute(builder: (context) => CartScreen())),
color: Colors.black,
),
)
],
As you can see, the selectedItems.length is the number of items in my cart.
Please help me out. Stuck here for a long time now...