How to create alert dialog with text field using GetX in flutter - flutter

I am able to create popup dialogs using Get.defaultDialog() function. I could not find a method in GetX for creating custom alert dialogs.
I am using the function below to achieve that which uses native flutter showDialog api.
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: categoryNameController,
keyboardType: TextInputType.text,
maxLines: 1,
decoration: InputDecoration(
labelText: 'Category Name',
hintMaxLines: 1,
border: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.green, width: 4.0))),
),
SizedBox(
height: 30.0,
),
RaisedButton(
onPressed: () {
if (categoryNameController.text.isNotEmpty) {
var expenseCategory = ExpenseCategory(
categoryNameController.text,
id: _addExpenseController.expenseCategories.length);
_expenseController.addExpenseCategory(expenseCategory);
_addExpenseController.expenseCategories
.add(expenseCategory);
Get.back();
}
},
child: Text(
'ADD CATEGORY',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
color: Colors.redAccent,
)
],
),
elevation: 12.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)));
});

I am able to achieve that using below function
Get.defaultDialog(
title: '',
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: settingsScreenController.categoryNameController,
keyboardType: TextInputType.text,
maxLines: 1,
decoration: InputDecoration(
labelText: 'Category Name',
hintMaxLines: 1,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green, width: 4.0))),
),
SizedBox(
height: 30.0,
),
RaisedButton(
onPressed: () {
if (settingsScreenController
.categoryNameController.text.isNotEmpty) {
var expenseCategory = ExpenseCategory(
settingsScreenController.categoryNameController.text,
id: _addExpenseController.expenseCategories.length);
settingsScreenController.addExpenseCategory(expenseCategory);
_addExpenseController.expenseCategories.add(expenseCategory);
Get.back();
} else {
Utils.showSnackBar("Enter category name");
}
},
child: Text(
'ADD CATEGORY',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
color: Colors.redAccent,
)
],
),
radius: 10.0);
But i could not remove the title area in the dialog.
Native API dialog without title
GetX dialog api with title

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main(List<String> args) {
runApp(GetMaterialApp(
title: 'My App',
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Show Dialog'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Get.defaultDialog(
);
},
child: Text('Click GetX')),
],
),
),
);
}
}

Related

Space bar key doesn't work on textfields ( Flutter Web )

In our application, SOMETIMES space input doesn't work in textFields (f.e. adding a new message in a chat), but after refreshing the page it works again. This situation is causing unstable environment for our users and thus a lot of complaints. Is there any official documentation, or solution by flutter for this "sometimes space button cannot be used" problem. What we notices that, when this problem occurs, fonts are different than it supposed to be, like it's not loaded. It is not browser specific because problem occurs at Chrome & Edge.
The real problem is that the bug is not easy to detect because it happens only few times so i don't know how to detect and solve it.
In particular i noted that it happens only in production ( i never spotted it during development) .
Flutter doctor:
This is an example widget that have the bug
Widget build(BuildContext context) {
return CustomAlertDialog(
title: const Text(
"Aggiungi un messaggio",
style: TextStyle(fontSize: 16),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Annulla",
style: TextStyle(color: Colors.grey.shade700),
)),
TextButton(
onPressed: () {
},
child: const Text("Invia"))
],
content: Container(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
border: Border.all(
color: Colors.green,
width: 2,
),
),
width: 800,
child: TextField(
minLines: 8,
maxLines: 8,
autofocus: false,
controller: _chatController,
//keyboardType: TextInputType.multiline,
onChanged: (text) {
setState(() {
textValue = text;
});
},
decoration: const InputDecoration(
contentPadding: EdgeInsets.all(8),
hintText: "Scrivi qui il tuo messaggio",
border: InputBorder.none,
),
),
),
SizedBox(
height: 10,
),
CheckboxListTile(
value: _hiddenMessage,
onChanged: (value) {
setState(() {
_hiddenMessage = value!;
});
},
title: const Text("Nascondi"),
)
],
),
),
));
}
CustomAlertDialog:
class _CustomAlertDialogState extends State<CustomAlertDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
insetPadding: widget.insetPadding ??
EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
title: widget.title ??
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(
Icons.cancel,
color: Colors.red,
size: 30,
),
SizedBox(width: 5),
Text('Errore'),
],
),
elevation: widget.elevation ?? 24.0,
actions: widget.actions ??
[
TextButton(
child: const Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
content: widget.content ??
const Text(
"Errore di connessione al server",
style: TextStyle(fontSize: 20),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
);
}
}
Thank you for the help!
Widget build(BuildContext context) {
return MaterialApp(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.space): ActivateIntent(),
},
title: 'title of your app',
//....
);
}

Flutter fixed button position

I'm developing an application on flutter and I'm having a problem with the position of the ElevatedButton. When the Validator returns the error message below the TextFormField, the widget expands downward and the position of the add button changes. I would like to keep the add button pinned to the same position as the beginning of the app
button in normal position
Button out of its original position after returning the validator
My code:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Lista de Compras"),
backgroundColor: Colors.green,
centerTitle: true,
),
body: Column(
children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(15, 10, 5, 10),
child: Form(
key: _formKey,
child: Row(
children: <Widget>[
Theme(
data: ThemeData(
primaryColor: Colors.green,
hintColor: Colors.green),
child: Expanded(
child: TextFormField(
controller: _controlador,
validator: (value) {
if (value.isEmpty) {
return "Insira um item";
}
return null;
},
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.green),
borderRadius:
BorderRadius.circular(100)),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(100)),
labelText: "Novo item",
hintText: "Insira um item",
hintStyle: TextStyle(color: Colors.grey),
labelStyle:
TextStyle(color: Colors.green),
suffixIcon: IconButton(
onPressed: () => _controlador.clear(),
icon: Icon(Icons.clear,
color: Colors.grey),
))))),
Padding(padding: EdgeInsets.only(right: 6)),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.green,
shape: CircleBorder(),
padding: EdgeInsets.all(12)),
child: Icon(Icons.add, color: Colors.white),
onPressed: () {
if (_formKey.currentState.validate()) {
_addCompras();
}
}),
],
),
)),
This is an expected behaviour, when TextFormField show an errorText this will append a text below the TextFormField adding extra height for about 22. Check working example below :
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
children: [
MeasureSize(
child: FormWidget(),
onChange: (size) {
print(size);
},
),
MeasureSize(
child: FormWidget(hideError: false),
onChange: (size) {
print(size);
},
),
FormWidget(
hideError: false,
addPaddingToTrailingButton: true,
),
],
),
),
);
}
}
class FormWidget extends StatelessWidget {
final bool hideError;
final bool addPaddingToTrailingButton;
FormWidget({
this.hideError = true,
this.addPaddingToTrailingButton = false,
});
#override
Widget build(BuildContext context) {
Widget trailingButton = ElevatedButton(
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(12),
),
child: Icon(Icons.add),
onPressed: () {},
);
if (addPaddingToTrailingButton) {
trailingButton = Padding(
padding: const EdgeInsets.only(bottom: 22),
child: trailingButton,
);
}
return Container(
color: Colors.grey[300],
margin: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 8),
Expanded(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
),
labelText: "Label text field",
hintText: "Hint text field",
errorText: hideError ? null : 'Error text shown',
suffixIcon: IconButton(
onPressed: () {},
icon: Icon(Icons.clear),
),
),
),
),
SizedBox(width: 8),
trailingButton,
],
),
);
}
}
typedef void OnWidgetSizeChange(Size size);
class MeasureSizeRenderObject extends RenderProxyBox {
Size oldSize;
final OnWidgetSizeChange onChange;
MeasureSizeRenderObject(this.onChange);
#override
void performLayout() {
super.performLayout();
Size newSize = child.size;
if (oldSize == newSize) return;
oldSize = newSize;
WidgetsBinding.instance.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;
const MeasureSize({
Key key,
#required this.onChange,
#required Widget child,
}) : super(key: key, child: child);
#override
RenderObject createRenderObject(BuildContext context) {
return MeasureSizeRenderObject(onChange);
}
}
I have adding a background so it will show the height difference :
TextField without errorText shown.
TextField with errorText shown.
TextField with errorText shown, with additional padding on icon button.
Check dartpad here
the answer by Allan above is very great.
But if you are facing issues learning that do it this way:
Add Elevated button to Column and add SizedBox with height 22 as its children. Here the height is calculated as (Height of TextFormField with error - of TextFormField without error) using flutter inspector.
Column(
children: [
ElevatedButton(...),
SizedBox(
height: 22,
),
],
),
Now add helperText: ' ', in TextFormField InputDecoration:
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return "Insira um item";
}
return null;
},
decoration: InputDecoration(
helperText: ' ',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
borderRadius: BorderRadius.circular(100)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(100)),
labelText: "Novo item",
hintText: "Insira um item",
hintStyle: TextStyle(color: Colors.grey),
labelStyle: TextStyle(color: Colors.green),
),
),
Here you can see the dartpad for full code.
This will get you this result:

Flutter: Carrying over data from one file to another

I have two files...Workout.dart and Submit.dart. In Workout.dart I have variables: name, sets, rep, pmg, and smg. The use will input things for the variables and then once they click submit, a new page will open displaying the data. However, I don't know how to carry over that data. I'm new to Flutter and Dart, Please help!
Workout.dart
import 'package:flutter/material.dart';
import 'package:gyminprogress/Submit.dart';
class This extends StatefulWidget{
#override
Workout createState() => Workout();
}
class Workout extends State<This> {
String name , sets , rep , pmg , smg ;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(
title: Text('Create Workouts'),
centerTitle: true,
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: 'Exercise Name'
),
style: TextStyle(color:Colors.white),
onChanged: (value){
setState(() {
name = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(
hintText: 'Primary Muscle Group'
),
style: TextStyle(color:Colors.white),
onChanged: (value){
setState(() {
pmg = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(
hintText: 'Secondary Muscle Group'
),
style: TextStyle(color:Colors.white),
onChanged: (value){
setState(() {
smg = value;
});
},
),SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(
hintText: 'Reps'
),
style: TextStyle(color:Colors.white),
onChanged: (value){
setState(() {
rep = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(
hintText: 'Sets'
),
style: TextStyle(color:Colors.white),
onChanged: (value){
setState(() {
sets = value;
});
},
),
Expanded(
child: Container(
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Colors.cyan[200],
padding: EdgeInsets.symmetric(
horizontal: 80.0,
vertical: 20.0
,),
child: Text('Submit',style: TextStyle(
color: Colors.black,fontWeight: FontWeight.w200
),) ,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Submit (
)),
);
},
),
]
),]),)
)
]
)
),
),
);
}
}
Submit.dart
import 'package:flutter/material.dart';
import 'package:gyminprogress/Workout.dart';
class Submit extends StatefulWidget{
#override
Enter createState() => Enter();
}
class Enter extends State<Submit> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(
title: Text('Create Workouts'),
centerTitle: true,
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text('Exercise Name : ${Workout.name}',
style: TextStyle(fontSize: 20.0,color: Colors.white, fontWeight: FontWeight.w200),),
Text('Primary Muscle Group : ${Workout.pmg} ',
style: TextStyle(fontSize: 20.0,color: Colors.white, fontWeight: FontWeight.w200),),
Text('Secondary Muscle Group : ${Workout.smg} ',
style: TextStyle(fontSize: 20.0,color: Colors.white, fontWeight: FontWeight.w200),),
Text('Reps : ${Workout.rep}',
style: TextStyle(fontSize: 20.0,color: Colors.white, fontWeight: FontWeight.w200),),
Text('Sets : ${Workout.sets}',
style: TextStyle(fontSize: 20.0,color: Colors.white, fontWeight: FontWeight.w200),),
]
)
)
)
);
}
}
To pass data from one widget to another you have to do two things,
Create a constructor of widget having data which it will accept,
Pass that data while calling it from another widget.
here is the code which solves your problem:
workout.dart
import "package:flutter/material.dart";
import './submit.dart';
class Workout extends StatefulWidget {
#override
_WorkoutState createState() => _WorkoutState();
}
class _WorkoutState extends State<Workout> {
String name, sets, rep, pmg, smg;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text('Create Workouts'),
centerTitle: true,
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Scaffold(
body: Column(children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Exercise Name'),
style: TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
name = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration:
InputDecoration(hintText: 'Primary Muscle Group'),
style: TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
pmg = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration:
InputDecoration(hintText: 'Secondary Muscle Group'),
style: TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
smg = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(hintText: 'Reps'),
style: TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
rep = value;
});
},
),
SizedBox(
height: 40.0,
),
TextField(
decoration: InputDecoration(hintText: 'Sets'),
style: TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
sets = value;
});
},
),
Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Colors.cyan[200],
padding: EdgeInsets.symmetric(
horizontal: 80.0,
vertical: 20.0,
),
child: Text(
'Submit',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600),
),
onPressed: () {
//String name, sets, rep, pmg, smg;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Submit(
name: name,
sets: sets,
rep: rep,
pmg: pmg,
smg: smg,
),
),
);
},
),
]),
]),
))
]))),
),
),
),
);
}
}
submit.dart
import "package:flutter/material.dart";
class Submit extends StatelessWidget {
final String name, sets, rep, pmg, smg;
const Submit({
Key key,
this.name = "",
this.sets = "",
this.rep = "",
this.pmg = "",
this.smg = "",
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(
title: Text('Create Workouts'),
centerTitle: true,
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(children: <Widget>[
Text(
'Exercise Name : ${name}',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.w200),
),
Text(
'Primary Muscle Group : ${pmg} ',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.w200),
),
Text(
'Secondary Muscle Group : ${smg} ',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.w200),
),
Text(
'Reps : ${rep}',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.w200),
),
Text(
'Sets : ${sets}',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.w200),
),
]))));
}
}
Output:
New Updated code
Output:
You can pass data in the navigator.
class Workout extends ....> {
String name;
Person(this.name);
}
// Navigate to second screen with data
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondScreenWithData(person: new Person("Vishal"))));
Then in the second screen
class SecondScreenWithData extends StatelessWidget {
// Declare a field that holds the Person data
final Person person;
// In the constructor, require a Person
SecondScreenWithData({Key key, #required this.person}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Second Screen With Data"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
// Display passed data from first screen
Text("name: ${person.name}"),
RaisedButton(
child: Text("Back to previous screen"),
onPressed: () {
// Navigate back to first screen when tapped!
Navigator.pop(context);
}
),
],
)
),
);
}

Flutter bottom sheet changing background heigth

I am using bottom sheet in my app but its changing the background screen height. I need my background page fixed. And need to increase bottom sheet height
Here is my bottom sheet code
import "package:flutter/material.dart";
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
class addQuestion extends StatefulWidget {
#override
_addQuestionState createState() => _addQuestionState();
}
class _addQuestionState extends State<addQuestion> {
void _submitData(){
Navigator.of(context).pop();
}
#override
Widget build(BuildContext context) {
double stackHeight = (MediaQuery.of(context).size.height);
return Container(
decoration: BoxDecoration(
color: Color(0xff404040),
borderRadius: BorderRadius.only(topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0))
),
child: SingleChildScrollView(
child: Card(
elevation: 5,
color: Color(0xff404040),
child: Container(
padding: EdgeInsets.only(top: 10, left: 10, right:10,
bottom: MediaQuery.of(context).viewInsets.bottom + 10 ,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
maxLength: 56,
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
decoration: InputDecoration(
counterText: '',
labelText: 'Would Question',
labelStyle: TextStyle(
color: Colors.blue
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
),
),
onSubmitted: (_) => _submitData(),
),
TextField(
maxLength: 56,
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
decoration: InputDecoration(
counterText: '',
labelText: 'Rather Question',
labelStyle: TextStyle(
color: Colors.red
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red),
),
),
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData(),
),
RaisedButton(onPressed: () {
_submitData();
}, child: Text('Add Question', style: TextStyle(
color: Colors.white
),),color: Theme.of(context).primaryColor,
),
],
),
),
),
),
);
}
}
As you see when keyboard open the background page is moving on the upside. And in rounded orders of bottom sheet, it's showing white corner don't know why its showing the need to transparent or remove this.
There are two kinds of bottom sheets in material design:
Persistent. A persistent bottom sheet shows information that supplements the primary content of the app. A persistent bottom sheet remains visible even when the user interacts with other parts of the app. Persistent bottom sheets can be created and displayed with the ScaffoldState.showBottomSheet function or by specifying the Scaffold.bottomSheet constructor parameter.
Modal. A modal bottom sheet is an alternative to a menu or a dialog and prevents the user from interacting with the rest of the app. Modal bottom sheets can be created and displayed with the showModalBottomSheet function.
showBottomSheet method
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: const Text('showBottomSheet'),
onPressed: () {
Scaffold.of(context).showBottomSheet<void>(
(BuildContext context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('BottomSheet'),
RaisedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
);
},
);
},
),
);
}
}
**showModalBottomSheet function **
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: const Text('showModalBottomSheet'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
RaisedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
);
},
);
},
),
);
}
}
The BottomSheet widget itself is rarely used directly. Instead, prefer to create a persistent bottom sheet with ScaffoldState.showBottomSheet or Scaffold.bottomSheet, and a modal bottom sheet with showModalBottomSheet.

Why does a change in state of a child bottom sheet trigger a rebuild of parent widget?

I have a Scaffold screen (ListsScreen).
Which has a Button(AddNewListButton) that opens up a Modal Bottom Sheet (ListScreenBottomSheetWidget).
Bottom Sheet has TextField (ListTitleInputTextFieldWidget).
When i tap the TextField to open the keyboard, the parent screen rebuilds itself, due to which ofcourse all its child widgets are rebuilt as well.
Why is this happening? I was under the impression that state changes only rebuild themselves or their children, not their parents. And i also added const constructors almost everywhere to avoid rebuilds but this is still happening.
The Parent ListsScreen:
class ListsScreen extends StatelessWidget {
const ListsScreen();
static const routeName = '/lists-screen';
#override
Widget build(BuildContext context) {
final user = Provider.of<AuthProvider>(context, listen: false).getUser;
print('stateless rebuilding');
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text(
'${user['name']}\'s Lists',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
actions: <Widget>[
const SignOutButton(),
],
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
SizeConfig.smallDevice
? Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const AddNewListButton(),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const UserListsListViewWidget(),
],
),
),
),
);
}
}
class SignOutButton extends StatelessWidget {
const SignOutButton();
Future<void> _submitRequest(BuildContext context) async {
_showLoadingAlert(context);
try {
await Provider.of<AuthProvider>(context, listen: false)
.submitLogOutRequest();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
} on HttpExceptions catch (error) {
Navigator.of(context).pop();
_showErrorDialogue(error.getErrorList, context);
}
}
void _showErrorDialogue(List<dynamic> errorMessages, BuildContext context) {
showDialog(
context: context,
builder: (ctx) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: ErrorListWidget(errorMessages: errorMessages),
),
);
}
void _showLoadingAlert(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => const LoadingWidget(),
);
}
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () => _submitRequest(context),
child: Row(
children: <Widget>[
Text(
'Sign Out',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
Icon(
Icons.exit_to_app,
color: Theme.of(context).primaryColorLight,
),
],
),
);
}
}
class AddNewListButton extends StatelessWidget {
const AddNewListButton();
void _modalBottomSheetMenu(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (builder) {
return const ListScreenBottomSheetWidget();
},
);
}
#override
Widget build(BuildContext context) {
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () => _modalBottomSheetMenu(context),
child: Text(
'+ Add List',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
);
}
}
The Modal Bottom Sheet:
import 'package:flutter/material.dart';
import 'package:todo_spicotech/helpers/size_config.dart';
class ListScreenBottomSheetWidget extends StatelessWidget {
const ListScreenBottomSheetWidget();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
currentFocus.unfocus();
},
child: Container(
margin: const EdgeInsets.all(20.0),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Material(
borderRadius: BorderRadius.all(Radius.circular(15)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizeConfig.smallDevice
? Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
const ListTitleInputTextFieldWidget(),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
borderRadius: BorderRadius.circular(5),
onTap: () {
Navigator.of(context).pop();
},
child: Ink(
padding: EdgeInsets.all(10),
child: const Text('CANCEL'),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () {},
child: Text(
'Create',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
),
],
),
],
),
),
),
),
);
}
}
class ListTitleInputTextFieldWidget extends StatefulWidget {
const ListTitleInputTextFieldWidget();
#override
_ListTitleInputTextFieldWidgetState createState() => _ListTitleInputTextFieldWidgetState();
}
class _ListTitleInputTextFieldWidgetState extends State<ListTitleInputTextFieldWidget> {
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
labelText: 'List Title',
contentPadding: EdgeInsets.all(10),
),
);
}
}
when you call showModalBottomSheet , it actually use Navigator inside
return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder,
source code of showModalBottomSheet https://github.com/flutter/flutter/blob/17079f26b54c8517678699a0cefe5f7bfec67b3f/packages/flutter/lib/src/material/bottom_sheet.dart#L635
Flutter teams' reply of issue Pages on Navigator stack rebuild when a new page is pushed https://github.com/flutter/flutter/issues/11655#issuecomment-348287396
This is working as intended. In general, you should assume that all widgets can rebuild at any time, that they don't is mostly just an optimisation.
In particular, routes will rebuild because their navigator state has changed so they might need to update how they draw back buttons and the like.