Rid of elevation of Nested Flutter Navigator 2.0 - flutter

Try to use Navigation 2.0 for a web project. I added a nested navigator, but I do not like the elevation that comes with the nested Navigator.
Ugly elevation
import 'package:flutter/material.dart';
import 'package:move_to_background/move_to_background.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Router(
routerDelegate: AuthenticationRouterDelegate(),
backButtonDispatcher: RootBackButtonDispatcher(),
),
);
}
}
class AuthenticationRouterDelegate extends RouterDelegate with ChangeNotifier {
bool isAuthenticated = false;
final GlobalKey<NavigatorState> navigatorKey;
AuthenticationRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
#override
Future<bool> popRoute() async {
print('popRoute AuthenticationRouterDelegate');
MoveToBackground.moveTaskToBack();
return true;
}
#override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
observers: [HeroController()],
pages: [
MaterialPage(
key: ValueKey('MyHomePage'),
child: MyAuthenticationWidget(
onPressed: () {
isAuthenticated = true;
notifyListeners();
},
),
),
if (isAuthenticated)
MaterialPage(
key: ValueKey('NestedNavigatorPage'),
child: NestedRouterWidget(),
),
],
onPopPage: (route, result) {
print('onPopPage AuthenticationRouterDelegate');
if (!route.didPop(result)) return false;
isAuthenticated = false;
notifyListeners();
return true;
},
);
}
// We don't use named navigation so we don't use this
#override
Future<void> setNewRoutePath(configuration) async => null;
}
class MyAuthenticationWidget extends StatelessWidget {
final VoidCallback onPressed;
MyAuthenticationWidget({required this.onPressed}) : super();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Side block'),
],
),
),
Expanded(
flex: 2,
child: Center(
child: NestedRouterWidget(),
),
)
],
),
);
}
}
class NestedRouterWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final childBackButtonDispatcher =
ChildBackButtonDispatcher(Router.of(context).backButtonDispatcher!);
childBackButtonDispatcher.takePriority();
return Router(
routerDelegate: NestedRouterDelegate(),
backButtonDispatcher: childBackButtonDispatcher,
);
}
}
final GlobalKey<NavigatorState> _nestedNavigatorKey =
GlobalKey<NavigatorState>();
class NestedRouterDelegate extends RouterDelegate with ChangeNotifier {
int selectedIndex = 0;
#override
Future<bool> popRoute() async {
print('popRoute NestedRouterDelegate');
return false;
}
#override
Widget build(BuildContext context) {
return Navigator(
key: _nestedNavigatorKey,
observers: [HeroController()],
pages: [
if (selectedIndex == 0)
MaterialPage(
key: ValueKey('ProfilePage'),
child: ProfileWidget(
onPressed: () {},
),
),
if (selectedIndex == 1)
MaterialPage(
key: ValueKey('NestedNavigatorPage'),
child: SettingWidget(),
),
],
onPopPage: (route, result) {
print('onPopPage NestedRouterDelegate');
return false;
},
);
}
// We don't use named navigation so we don't use this
#override
Future<void> setNewRoutePath(configuration) async => null;
}
class ProfileWidget extends StatelessWidget {
final VoidCallback onPressed;
ProfileWidget({required this.onPressed}) : super();
#override
Widget build(BuildContext context) {
// omit
}
}
class SettingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
// omit
}
}
Full source code on GitHub here https://github.com/AndrewPiterov/flutter_web_nested_navigator/blob/main/lib/main.dart
How to remove this elevation? Thanks!

At the end, figured out the solution is to set fullscreenDialog to true
MaterialPage(
key: ValueKey('ProfilePage'),
fullscreenDialog: true,
child: ProfileWidget(
onPressed: () {},
),
),

Related

How to avoid GlobalKey<NavigatorState> dublicate keys in flutter

I have nested navigation. When I want to navigate to the home page, I face with dublicate key errors.
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
List<GlobalKey<NavigatorState>> navigatorKeys = [
mainNavigatorKey,
menuNavigatorKey
];
Future<bool> _systemBackButtonPressed() async {
if (navigatorKeys[_selectedIndex].currentState?.canPop() ?? false) {
navigatorKeys[_selectedIndex]
.currentState
?.pop(navigatorKeys[_selectedIndex].currentContext);
}
return false;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _systemBackButtonPressed,
child: Scaffold(
backgroundColor: Colour.gray6,
bottomNavigationBar:
BottomNavigationMenu((val) => setState(() => _selectedIndex = val)),
body: FadeIndexedStack(
index: _selectedIndex,
children: const [
MainNavigator(),
MenuNavigator(),
],
),
),
);
}
GlobalKey<NavigatorState> mainNavigatorKey = GlobalKey<NavigatorState>();
class _MainNavigatorState extends State<MainNavigator> {
#override
Widget build(BuildContext context) {
return Navigator(
key: mainNavigatorKey,
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute()
Because of this hierarchie I can not navigate to Home page. when I do Navigator.of(context).pushNamed(RouteNames.home) I get an error. How to solve this ?

how to use a shared preference from a change notifier

I'm trying to get my head around the shared preference package from a change notifier. here is my code:
Future<void> main() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => GlobalSettings1(prefs: prefs)),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/': (context) => Page01(),
});
}
}
class Page01 extends StatefulWidget {
const Page01({Key? key}) : super(key: key);
#override
State<Page01> createState() => _Page01State();
}
class _Page01State extends State<Page01> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 30,),
InkWell(
onTap: () {
context.read<GlobalSettings1>().ToggleSwitch();
},
child: Container(
height: 30,
width: 80,
color: context.watch<GlobalSettings1>().toggleSwitch01
? Colors.green
: Colors.red,
),
),
],
),
);
}
}
and here is my change notifier:
class GlobalSettings1 with ChangeNotifier {
bool _toggleSwitch01 = true;
final SharedPreferences prefs;
GlobalSettings1({required this.prefs});
bool get toggleSwitch01 => _toggleSwitch01;
void ToggleSwitch() {
_toggleSwitch01 = !_toggleSwitch01;
_setPrefItems();
notifyListeners();
}
void _setPrefItems() {
prefs.setBool('toggleSwitch01', _toggleSwitch01);
notifyListeners();
}
void _getPrefItems() {
_toggleSwitch01 = prefs.getBool('toggleSwitch01') ?? true;
notifyListeners();
}
bool getToggleSwitch01() {
_getPrefItems();
return _toggleSwitch01;
}
}
How do i use setprefitems and getprefitems in my code to make it so the toggleswitch01 bools state is saved for when the app is closed and then statrted again?
thanks so much

Animated moveable list in flutter?

any tips or help how can I make this on tap moveable list in flutter?
https://files.fm/f/txdn29dg3
The provided component is exactly what CupertinoPicker could offer you.
Also, as suggested in the documentation, you should combine the CupertinoPicker with showCupertinoModalPopup to display the picker modally at the bottom of the screen.
This is how the code could look like:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: PickerPage(),
),
),
);
}
}
class PickerPage extends StatefulWidget {
const PickerPage();
#override
_PickerPageState createState() => _PickerPageState();
}
class _PickerPageState extends State<PickerPage> {
final _items = [
'Flat Rate',
'Hourly',
'Request for Price',
];
int _selectedItem = 0;
void _onSelectedItemChanged(int value) => setState(
() => _selectedItem = value,
);
void _showPicker() {
showCupertinoModalPopup(
context: context,
builder: (_) => PickerExample(
items: _items,
selectedItem: _selectedItem,
onSelectedItemChanged: _onSelectedItemChanged,
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_items[_selectedItem]),
const SizedBox(height: 10.0),
ElevatedButton(
child: const Text('Show picker'),
onPressed: _showPicker,
),
],
);
}
}
class PickerExample extends StatefulWidget {
final List<String> items;
final int selectedItem;
final ValueSetter<int> onSelectedItemChanged;
const PickerExample({
required this.items,
required this.selectedItem,
required this.onSelectedItemChanged,
});
#override
_PickerExampleState createState() => _PickerExampleState();
}
class _PickerExampleState extends State<PickerExample> {
late final FixedExtentScrollController _controller;
#override
void initState() {
super.initState();
_controller = FixedExtentScrollController(initialItem: widget.selectedItem);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
child: CupertinoPicker(
scrollController: _controller,
backgroundColor: Colors.white,
itemExtent: 30.0,
children: [
for (final item in widget.items) Center(child: Text(item)),
],
onSelectedItemChanged: widget.onSelectedItemChanged,
),
);
}
}
You could also find an interactive example in this DartPad.

Flutter : sending data across multiple screens

I have 3 Widget MyApp Widget ,Home Widget, and Sliver Appbar Widget, It's connected to each other. Example MyApp Widget -> Home Widget -> SliverAppbar Widget.
My question is , how to Passing data from My App Widget directly to SliverAppBar Widget ?
I found what i think it's can solve my case that is Inherited Widget. But i confused to understading to use this widget.
I already try using Inherited Widget as documentation like this :
MyApp Widget
class SettingsApp extends InheritedWidget {
SettingsApp({Key key, this.isDarkMode = false, Widget child})
: super(key: key, child: child);
final bool isDarkMode;
static SettingsApp of(BuildContext context) {
return (context.dependOnInheritedWidgetOfExactType<SettingsApp>());
}
#override
bool updateShouldNotify(SettingsApp oldWidget) {
return true;
}
}
SliverAppBar Widget
class SliverAppBarCustom extends StatelessWidget {
final Box detbBox = Hive.box("debt_box");
final UserModelHive userModelHive = Hive.box("user_box").get("userSession");
#override
Widget build(BuildContext context) {
final isDarkMode =
context.dependOnInheritedWidgetOfExactType<SettingsApp>().isDarkMode;
print(isDarkMode.toString());
var mediaQuery = MediaQuery.of(context);
var textTheme = Theme.of(context).textTheme;
return Text(isDarkMode.toString());
}
}
But i get this error :
Log
The following NoSuchMethodError was thrown building SliverAppBarCustom(dirty):
The getter 'isDarkMode' was called on null.
Receiver: null
Tried calling: isDarkMode
Using ScopedModel
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
void main() => runApp(MyApp());
class SettingsModel extends Model {
bool _isDarkMode;
SettingsModel({bool isDarkMode}) : _isDarkMode = isDarkMode ?? false;
bool get isDarkModel => _isDarkMode;
set isDarkModel(bool value) {
_isDarkMode = value;
notifyListeners();
}
void switchTheme() {
_isDarkMode = !_isDarkMode;
notifyListeners();
}
static SettingsModel of(BuildContext context) {
return ScopedModel.of<SettingsModel>(context);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScopedModel<SettingsModel>(
model: SettingsModel(isDarkMode: true),
child: MaterialApp(
home: InitPage(),
),
);
}
}
class InitPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Init Page")),
body: SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScopedModelDescendant<SettingsModel>(
builder: (context, child, model) {
return Text('Is Dark Mode: ${model.isDarkModel}');
},
),
RaisedButton(
child: Text("Next Page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
},
),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Page"),
),
body: SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScopedModelDescendant<SettingsModel>(
builder: (context, child, model) {
return Text('Is Dark Mode: ${model.isDarkModel}');
},
),
RaisedButton(
child: Text("Switch Theme"),
onPressed: SettingsModel.of(context).switchTheme,
),
],
),
),
);
}
}
Important: You should not change _isDarkModel without notifyListeners(). If you do UI may not update.

Flutter switch between fragments by supporting back to previous fragment

in this link in SF, #martinseal1987 show us how can we use separated widgets link with android fragments.
I implemented this solution on my project and after running project i dont have any problem to show first widgets as an Fragment, but when i press to back button my screen goes to black and couldn't back to previous widgets as an fragment
i think that is should be this:
Problem is on navigateBack and customPop methods and i can attach fragment by pressing on button
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
title: 'AndroidMonks',
home: Scaffold(
appBar: AppBar(
title: Text('Androidmonks'),
backgroundColor: Colors.orangeAccent,
),
body: Home(),
),
));
}
class Home extends StatefulWidget {
Home({
Key key,
}) : super(key: key);
#override
State<Home> createState()=>_Home();
}
class _Home extends State<Home> {
String title = "Title";
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
navigateTo(_currentIndex);
//each fragment is just a widget which we pass the navigate function
List<Widget> _fragments =[Fragment1(),Fragment2(),Fragment3()];
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
customPop(context);
},
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child:Text('PRESS'),
onPressed: (){
_currentIndex++;
navigateTo(_currentIndex);
},
),
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
customPop(BuildContext context) {
if (_backstack.length - 1 > 0) {
navigateBack(_backstack[_backstack.length - 1]);
} else {
_backstack.removeAt(_backstack.length - 1);
Navigator.pop(context);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}
class Fragment2 extends StatefulWidget {
#override
State<Fragment2> createState() => _Fragment2();
}
class _Fragment2 extends State<Fragment2> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("_Fragment2"),
onPressed: (){
}),
);
}
}
class Fragment1 extends StatefulWidget {
#override
State<Fragment1> createState() => _Fragment1();
}
class _Fragment1 extends State<Fragment1> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment1"),
);
}
}
class Fragment3 extends StatefulWidget {
#override
State<Fragment3> createState() => _Fragment3();
}
class _Fragment3 extends State<Fragment3> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment3"),
);
}
}
I fixed some logic in your code please carefully check the changes, if you have any question don't hesitate, here is the working code
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
title: 'AndroidMonks',
home: Scaffold(
appBar: AppBar(
title: Text('Androidmonks'),
backgroundColor: Colors.orangeAccent,
),
body: Home(),
),
));
}
class Home extends StatefulWidget {
Home({
Key key,
}) : super(key: key);
#override
State<Home> createState()=>_Home();
}
class _Home extends State<Home> {
String title = "Title";
List<Widget> _fragments =[Fragment1(),Fragment2(),Fragment3()];
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
//navigateTo(_currentIndex);
//each fragment is just a widget which we pass the navigate function
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
return customPop(context);
},
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child:Text('PRESS'),
onPressed: (){
_currentIndex++;
navigateTo(_currentIndex);
},
),
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
Future<bool> customPop(BuildContext context) {
print("CustomPop is called");
print("_backstack = $_backstack");
if (_backstack.length > 1) {
_backstack.removeAt(_backstack.length - 1);
navigateBack(_backstack[_backstack.length - 1]);
return Future.value(false);
} else {
return Future.value(true);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}
class Fragment2 extends StatefulWidget {
#override
State<Fragment2> createState() => _Fragment2();
}
class _Fragment2 extends State<Fragment2> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("_Fragment2"),
onPressed: (){
}),
);
}
}
class Fragment1 extends StatefulWidget {
#override
State<Fragment1> createState() => _Fragment1();
}
class _Fragment1 extends State<Fragment1> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment1"),
);
}
}
class Fragment3 extends StatefulWidget {
#override
State<Fragment3> createState() => _Fragment3();
}
class _Fragment3 extends State<Fragment3> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment3"),
);
}
}
You can achieve this type of navigation using LocalHistoryRoute.of(context).addLocalHistoryEntry and Navigator.pop().