How to show a persistant BottomModalSheet that remains in all tabs - flutter

I'm creating an app that requires a bottomModalSheet to stay persistant in all (Bottom) tabs regardless of which one I select.
I've gotten it to work on a single BottomTab, but once I click on the other it loses it's state and the Modal is gone as well.
I'm using GoRouter for routing, modal_bottom_sheet for the modalBottomSheet.
BTW I've added BottomSheet on the Main Scaffold.
Problem is I can't go on any other screen from that bottomSheet(They come behind it rather than on top of it)

You can use bottomSheet property of Scaffold.
class Sample extends StatefulWidget {
const Sample({Key? key}) : super(key: key);
#override
State<Sample> createState() => _SampleState();
}
class _SampleState extends State<Sample> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: [],),
),
body: TabBarView(children: [],),
// add your bottomModelSheet widget here
bottomSheet: MyCustomWidget(),
);
}
}

Related

Opening a text field causes the parrent widget to rebuild

my app is structured like this:
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
BlocProvider.of<NavigationBloc>(context).add(const DashboardEvent());
int _selectedIndex = 0;
return Scaffold(
appBar: AppBar(
elevation: 0,
The Main Page is responsible for navigation via the navigation bloc. Different buttons add different events depending on what page I want to show. Up to this point everything was working normally but all of sudden when I open any TextField in my app, it causes this main page to rebuild, calling the DashboardEvent and the page with the text field disappear.
I know that opening a TextField causes flutter to call the build method, but up to this point, in only caused the page to rebuild, not the parent widget. I have no idea why is it behaving like this all of sudden. Is there any setting I could toggle that I don't remember that could cause this behavior ? Thank you
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
initState(){
BlocProvider.of<NavigationBloc>(context).add(const DashboardEvent());
int _selectedIndex = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,

What exactly are GlobalKeys and keys in flutter?

I have tried to learn this concept for over a month and I just can't seem to figure out what exactly is GlobalKeys and various types of keys in simpler terms, Is it just for form validator or does it serve more purpose,There aren't clear basic example of this in youtube too or perhaps may be there is a book somewhere ?
Here in basic flutter app
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Here there are lines like
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
\\\\
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
What does Key /super keyword have to do with it or how do we use it?
I found this very helpful: https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d
I found two uses for GlobalKeys (I'm sure there's more):
First one: this is what the article above discusses: how to link a state object to a widget. I'm going to oversimplify few things here.
Normally, flutter will not use Global Keys. When you create a stateful widget, two object get created: a widget, and it's state. The idea is that the widget itself will be destroyed at the end of the build (or after it is painted on the screen). Once you initiate the build again (through setState() for exmaple) - a widget will be recreated.
Of course - you want your state object to persist between the changes - this is where you store your data after all. And you want Flutter to link back your state object to newly created Widget object instance.
Most of the time, this is an easy thing for Flutter - it will just find the same widget in the same position, link it to it's state object and it's done.
In case your widget moves around in the next build - this will not work, and new state object will be created and you will lose your state.
Take a look at this modified Flutter Counter app - I moved the counter into a separate Widget, but also linked it back to the the original _MyHomePageState. Each time we rebuild the main widget we will flip the order of the button and text box in the column - and you will see that the count never changes: it always show zero.
Since the widget changed the position, it's state is dropped and new one created.
You can run this in dartpad quickly:
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _key=GlobalKey();
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_counter.isOdd) Counter(onPressed: _incrementCounter),
const Text(
'You have pushed the button this many times:',
),
if (_counter.isEven) Counter(onPressed: _incrementCounter),
],
),
),
);
}
}
class Counter extends StatefulWidget {
final VoidCallback onPressed;
const Counter({Key? key, required this.onPressed}) : super(key: key);
#override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
widget.onPressed();
}
#override
Widget build(BuildContext context) {
return Column(children: [
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
ElevatedButton(
style:
ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20)),
onPressed: _incrementCounter,
child: const Text('Increment'),
)
]);
}
}
Now simply by adding a GlobalKey to Counter widget (in both calls), we help flutter link the state object. Note that we created the _key value once in the state object - since we want to pass the same key each time (otherwise what's the point...)
Counter(onPressed: _incrementCounter, key: _key),
And after this change, even when Widget changes the place and order on screen - your state is kept.
Now you would ask: why wouldn't Flutter do this by default for all the Widgets? Turns out - GlobalKeys are very expensive to maintain, and if you want to render 60 frames per second (16ms each frame) - you want to optimize every single step. By 'no keys by default' approach, everything is optimized, and works in large majority of the cases - but in few situations when you need global keys - you need to learn a little bit about it, and it is very simple to use.
Second use I found for it: you need global key to find the your exact Widget position on screen after it renders. Widget itself will not know where it will end up, you only know this after the rendering is done. You need this if you want to do custom animation or something like that.
This is a simple function that will tell you where your widget is:
Rect getRectFromKey(GlobalKey key) {
RenderBox renderBox = (key.currentContext!.findRenderObject())! as RenderBox;
var targetPosition = renderBox.localToGlobal(Offset.zero);
var targetSize = renderBox.size;
// A Rect can be created with one its constructors or from an Offset and a Size using the & operator:
Rect rect = targetPosition & targetSize;
return rect;
}
Multiple widgets of the same type and at the same level in a widget tree may not update as expected unless they have unique keys, given that these widgets hold some state.
Explicitly setting a key to a widget helps Flutter understand which widget it needs to update when state changes.
keys also store and restore the current scroll position in a list of widgets.
Global key usually use to change parent widget from any portion of app considering state unchanged. Gobal keys are broad than keys in short.
Global keys are for Form validations. Other than global keys there is value,object,and unique key in Flutter. You get clear idea about all this through below link.
Keys in Flutter

AppBar not working and considered as dead code

This is my code, it keeps showing up as "Dead Code." and it wont show anything in my AVD, can anyone help me with this?
import 'package:flutter/material.dart';
class SignInScreen extends StatelessWidget {
const SignInScreen({Key? key}) : super(key: key);
static String routeName = "/sign_in";
#override
Widget build(BuildContext context) {
return Scaffold();
AppBar();
}
}
Firstly AppBar() should be inside the Scaffold widget.
The code should be something like this:
import 'package:flutter/material.dart';
class SignInScreen extends StatelessWidget {
const SignInScreen({Key? key}) : super(key: key);
static String routeName = "/sign_in";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar();
);
}
}
Secondly when you use return the code below the return statement doesn't execute, that is the reason your ide shows as a "dead code"
AppBar should be inside scaffold widget something like this.
try this:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Your Navigationbar title"),
),
body: Container(),
);
}
}
Also after return statement code never execute so. Your ide showing you dead code.

Flutter PageView not swipeable on web (desktop mode)

I'm new to flutter.
I implemented flutter PageView with the help of its documents:
/// Flutter code sample for PageView
// Here is an example of [PageView]. It creates a centered [Text] in each of the three pages
// which scroll horizontally.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final PageController controller = PageController(initialPage: 0);
return PageView(
/// [PageView.scrollDirection] defaults to [Axis.horizontal].
/// Use [Axis.vertical] to scroll vertically.
scrollDirection: Axis.horizontal,
controller: controller,
children: const <Widget>[
Center(
child: Text('First Page'),
),
Center(
child: Text('Second Page'),
),
Center(
child: Text('Third Page'),
)
],
);
}
}
And I run it on Android, it works well.
and It works also in web (mobile mode).
But when I run it on chrome (web|desktop) pages are not swipeable, and there is no way to change pages.
How to enable swipe on web desktop export?
Flutter version is 2.5.2
Thanks to Bigfoot, For support swipe with mouse, we need to change default scroll behavior of app by these steps:
1- Create a class, extend it from MaterialScrollBeavior, and override dragDevices:
class AppScrollBehavior extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
2- Pass an AppScrollBehavior instance to scrollBehavior property of MaterialApp:
MaterialApp(
scrollBehavior: AppScrollBehavior(),
...
);
After that, we can swipe between pages also with mouse.
Update Flutter 3.3:
From flutter 3.3, trackpad on laptops not working for scroll and swipe actions, if you need to support trackpad too, you need add PointerDeviceKind.trackpad to dragDevices like this:
class AppScrollBehavior extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
};
}
on flutter 2.5.0 they change the scroll behavior
check this Default drag scrolling devices
Without creating additional classes and other stuff, you can do this:
ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
},
),
child: MyScrollableWidget(...)
I used PageView in my Flutter portfolio website. I noticed that I couldn't swipe to change pages. Later I came to know that this could be done by my Mac's trackpad gestures.
Since the PageView was horizontal, I had to horizontally swipe two fingers on my trackpad. You can try that.

Process of developing a custom widget in Flutter

I am learning Android app development using Dart/Flutter and I am trying to understand the general process of developing a custom widget for an app. For example, if I need a widget that has a TextField, an Image, and a checkbox, how do I test that widget individually?
I mean, there is no app to put that widget in as of now, so how do I "execute" it to see if that widget is getting laid out correctly and working properly as expected?
In the Java Swing world, I would just put a main method in my component class. In that main method, I would create a frame or something and add that component to that frame. Then run that class directly. That way, I can basically fine tune the component without worrying about running the actual application, and without having to go through the whole app flow to reach that component and check how it looks.
Is it similar in Flutter app as well? Create a dummy app with the widget as the only "screen" and then execute that dummy app?
There is an excellent online tool called "Dart Pad", the link here should get you started on basic boilerplate code that is the beginnings of your app.
You can then proceed to create a custom widget, I'll try and replicate your example widget below:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
Color getColor(Set<MaterialState> states) {
const Set<MaterialState> interactiveStates = <MaterialState>{
MaterialState.pressed,
MaterialState.hovered,
MaterialState.focused,
};
if (states.any(interactiveStates.contains)) {
return Colors.blue;
}
return Colors.red;
}
return Container(
child: Column(
children: [
Image.network("https://icatcare.org/app/uploads/2018/07/Thinking-of-getting-a-cat.png"),
TextField(decoration: InputDecoration(hintText: "Type something")),
Checkbox(
checkColor: Colors.white,
fillColor: MaterialStateProperty.resolveWith(getColor),
value: isChecked,
onChanged: (bool? value) {
setState(
() {
isChecked = value!;
},
);
},
),
],
),
);
}
}