Customizing navigation route widget in flutter - flutter

I have a BottomNavigationBar in my widget, when I want to show a new widget inside one of the items with Navigator, It shows a new screen on my last widget.
How can I replace my new widget with items of BottomNavigationBar?
Main Widget State:
class _MainWidgetState extends State<MainWidget> {
int _currentIndex = 0;
final List<Widget> _children = [
HomeWidget(title: AppConstants.appName),
ExerciseWidget(title: AppConstants.exercise),
ProfileWidget(title: AppConstants.profile),
];
.
.
Profile Widget State:
class _ProfileWidgetState extends State<ProfileWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.settings),
onPressed: () {
ViewUtils.openWidget(context, SettingWidget());
},
),
),
);
}
openWidget in ViewUtils:
static void openWidget(BuildContext context, Widget widget) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => widget
),
);
}
I need to Navigator shows my SettingWidget like SupportFragmentManager in Android: getSupportFragmentManager().beginTransaction().replace(..).
How do I do this?
More Info
I have Splash Widget that use pushAndRemoveUntil with ModalRoute.withName('/main'); that Main Widget creates BottomNavigationBar with some items.
In one of them I click settings button which shows a new widget, this showing occurs with Navigator.of(context).push or Navigator.of(context).pushReplacement.
Both of them shows my new widget on top of my main widget not replacing to one of my items in BottomNavigationBar.

Related

can you navigate between widgets with only one scaffold

I have created a widget with a scaffold and called a widget in it as
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Everything Store"),
),
body: Register(),
);
}
}
the Register component doesn't have a scaffold in it, but when I try to navigate from the Register widget to another one that also doesn't have a scaffold too, so I used
onPressed: () => {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => login_page()))
},
I got an error as " No Material widget found. "
So is there a way to have one scaffold or should I make a scaffold for each widget?
The scaffold is one of the main widgets that helps you build up the UI screen on a device but a screen doesn't necessarily need it. The error that you are getting it might be because you are not passing the Widget class correctly.
Try to replace login_page() with the class name like so Register().

Flutter: Select an icon on an AlertDialog and show the new icon

I am trying to use an AlertDialog in flutter where the user can press a card to select an icon. Once they've selected the icon, the AlertDialog should show the newly selected icon.
Right now, I have it so that every time the user taps on the card, the card gets reloaded. However, this means that if I select an icon, I need to tap to select a second icon before it gets reloaded with my previous change.
All advice and/or different ideas on how to handle something like this are welcome.
Here is the code I have below:
import 'package:flutter/material.dart';
import 'package:test_003/data/dataStoreLegendItems.dart'; //has defaultIcon which should get updated
import 'package:test_003/dialogs/iconPickerDialog.dart';
class IconPickerCard extends StatefulWidget {
var alertDialogContext;
IconPickerCard({this.alertDialogContext});
#override
_IconPickerCardState createState() => _IconPickerCardState();
}
class _IconPickerCardState extends State<IconPickerCard> {
#override
Widget build(BuildContext context) {
return new Card(
child: ListTile(
leading: legendItems.defaultIcon,
title: Text('Select Icon'),
onTap: () async {
setState(() {
print("First line of IconPickerCard set state");
showIconPickerDialog(widget.alertDialogContext);
print('Icon Picker Card List Tile pressed');
});
},
),
);
}
}
This gets called by the Alert Dialog:
import 'package:flutter/material.dart';
import 'package:test_003/components/iconPickerForPopup.dart';
class ReuseAddPopup extends StatefulWidget {
#override
_ReuseAddPopupState createState() => _ReuseAddPopupState();
}
class _ReuseAddPopupState extends State<ReuseAddPopup> {
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
children: [
IconPickerCard(alertDialogContext: context),
],
),
);
}
}
This is what it looks like:
Alert Dialog
Then when the card is pressed:
IconPicker
After an icon is selected the changes do not get reflected on the card until the card is pressed again:
After Icon is selected
The context is different in overlay widgets such as modalBottomSheet and showDialog so the state does not rebuild, to make rebuild we have to wrap it with a StatefulBuilder widget like so:-
#override
Widget build(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter dialogState /*You can rename this!*/) {
return Card(
child: ListTile(
leading: legendItems.defaultIcon,
title: Text('Select Icon'),
onTap: () async {
dialogState(() {
print("First line of IconPickerCard set state");
showIconPickerDialog(widget.alertDialogContext);
print('Icon Picker Card List Tile pressed');
});
},
),
);
}
});
would recommend using this in the call to AlertDialog and make everything stateless.

Flutter Show Modal Bottom Sheet after build

As the title says, I have a String parameter and when I load the Home Stateful Widget I would like to open this bottom sheet if the parameter is not null.
As I understood I can't call showModalBottomSheet() in the build function of the Home widget because it can't start building the bottom sheet while building the Home Widget, so, is there a way to call this immediately after the Home Widget is built?
One of the solutions might be using addPostFrameCallback function of the SchedulerBinding instance. This way you could call showModalBottomSheet after the Home widget is built.
import 'package:flutter/scheduler.dart';
...
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
//Your builder code
},
);
});
//Return widgets tree for Home
}
Here's one way:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
child: Text('heyooo'),
);
}
);
});
return Scaffold(
appBar: AppBar(),
body: Container(),
);
}
}

Flutter persistent bottom navigation bar and page view replacement approach

I have a Navbar state full widget that tracks current page and returns a widget with a bottom navbar and dynamic body based of current page which is stored as a state
class _PullingoNavbarState extends State<PullingoNavbar> {
static int _page = 1;
final _screens = {0: PullingoMap(), 1: Dashboard(), 2: Dashboard()};
#override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_page],
bottomNavigationBar: CurvedNavigationBar(
animationDuration: Duration(milliseconds: 200),
backgroundColor: Colors.blueAccent,
index: _page,
items: <Widget>[
PullingoIcon(icon: Icons.favorite),
PullingoIcon(icon: Icons.chrome_reader_mode),
PullingoIcon(icon: Icons.person),
],
onTap: (index) {
setState(() {
_page = index;
});
},
),
);
}
}
and the root widget as follows:
class RoutesWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
title: 'PULLINGO',
theme: pullingoTheme,
routes: {
"/": (_) => PullingoNavbar(),
},
);
}
pre creating instances of _screens in a map doesn't feel like a good approach to me. this probably will create states of those screens regardless of user visits them or not. There are few suggestions given here. does the above method look okay or should I completely avoid this approach.
You can use PageView and AutomaticKeepAliveClientMixin to persist your widgets when navigating. With this approach a widget is only created when a user navigates to it by bottom navigation bar. I have recently written an article about how to use it, might be useful.
https://cantaspinar.com/persistent-bottom-navigation-bar-in-flutter/

Flutter : handling multiple navigation screen in CupertinoTabScaffold

Hello I am new to Flutter and I am trying to implement a bottom tab bar with multiple navigation screen for each tab.
Here is my initial set up
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return CupertinoApp(home: HomeScreen(),
routes: {
Screen1.id: (context) => Screen1(),
Screen2.id: (context) => Screen1(),
DetailScreen3.id: (context) => DetailScreen3(),
DetailScreen4.id: (context) => DetailScreen4(),
});
}
}
Here is my HomeScreen
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.book_solid),
title: Text('Articles'),
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.eye_solid),
title: Text('Views'),
),
],
),
tabBuilder: (context, index) {
if (index == 0) {
return Screen1();
} else {
return Screen2();
}
},
);
}
}
Here is my screen1
class Screen1 extends StatelessWidget {
static const String id = 'screen1';
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, DetailScreen3.id);
},
child: Center(
child: Text('Screen 1',),
),
),
);
}
}
and here is my screen3
class DetailScreen3 extends StatelessWidget {
static const String id = 'screen3';
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Center(
child: Text('terzo schermo',),
),
);
}
}
The tabBar work ok and I am able to swap between the 2 tabs but I am not able to navigate from screen 1 to screen 3. When I tap on screen1 Center widget, the screen start to navigate but half way it stops and then the screen become all black...
Here is the error
There are multiple heroes that share the same tag within a subtree.
Within each subtree for which heroes are to be animated (i.e. a
PageRoute subtree), each Hero must have a unique non-null tag. In this
case, multiple heroes had the following tag: Default Hero tag for
Cupertino navigation bars with navigator NavigatorState#05492(tickers:
tracking 2 tickers)
I understand the problem is related to the hero tag of the navigation bar which must have a unique identifier. How should I fix this problem? should I assign an heroTag to all navigation bar???
Many thanks in advance for the help
I resolved by setting the following properties for each CupertinoNavigationBar
heroTag: 'screen1', // a different string for each navigationBar
transitionBetweenRoutes: false,
As an iOS developer, I tried flutter for the first time, this thing caused a black screen after jumping the page, and also troubled me for two days
heroTag: 'screen1', // a different string for each navigationBar
transitionBetweenRoutes: false,