Related
i am trying to update the state of the animated switcher in the middle area. i am trying to do this using a setstate in the lower area. but it does not work.
the first thing i did is to create a variable with a boolean data type in the home class.
then i passed the variable to both the middle area and the lower area
the idea was that if the same variable is passed to the class whose state i am trying to update, and the class with the set state, it would work. but it seems i am wrong. i would appreciate some assistance.
the boolean variable i am trying to make work is the _rep variable
This is the Home widget
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late AnimationController _controller;
late Animation<Offset> _animation;
late Animation<Offset> _anime;
bool _rep = true;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2)
);
_animation = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,3.0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
_anime = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,-0.55),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
super.initState();
}
#override
void dispose() {
_animationController.dispose();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 3.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TopIcon(icons: Icons.arrow_back, color:Colors.grey.shade300 ,),
SizedBox(
height: 13.h,
width: 13.w,
child: Image.asset('assets/images/download.png')
),
TopIcon(icons: Icons.shopping_bag_outlined, color: Colors.grey.shade300,),
],
),
SizedBox(
height: 3.h,
),
Text('Frappuccino',
style: TextStyle(
fontSize: 27.sp,
fontWeight: FontWeight.bold
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('White Chocolate',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey.shade400
),
),
),
MiddleArea(
controller: _animationController,
animation: _animation,
rep: _rep,
),
LowerArea(controller: _animationController, anime: _anime, rep = _rep),
],
),
),
),
);
}
}
This is the middle area
class MiddleArea extends StatefulWidget {
MiddleArea({Key? key, required this.controller, required this.animation, required this.rep}) : super(key: key);
AnimationController controller;
Animation<Offset> animation;
final bool rep;
#override
State<MiddleArea> createState() => _MiddleAreaState();
}
class _MiddleAreaState extends State<MiddleArea> {
bool _flag = true;
bool _favourite = true;
#override
Widget build(BuildContext context) {
print(widget.rep);
return SizedBox(
height: 52.h,
child: Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 135.0),
child: Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade200,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade100,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade50,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
SizeAndFave(text: 'Preference'),
SizeAndFave(text: 'Fave!')
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: (){
setState(() {
_flag = !_flag;
});
},
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: widget.rep == true?Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
)
),
GestureDetector(
onTap: (){
setState(() {
_favourite = !_favourite;
});
},
child: _favourite? TopIcon(icons: Icons.favorite_border, color: Colors.brown.shade300)
:TopIcon(
icons: Icons.favorite, color: Colors.brown.shade300)
)
],
)
],
),
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition( opacity: animation,
child: child);
},
child: _flag == true ? Center(
key: const Key('1'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/starcup.png'),
),
),
):Center(
key: const Key('2'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/greeen.png'),
),
),
),
),
Positioned(
child:
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: TopIcon(
icons: Icons.car_crash_outlined, color: Colors.brown.shade300),
),
)),
const Positioned(
child:
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.only(top: 330.0, left: 14),
child: Text('\$ 5.99',
style: TextStyle(
fontSize: 27,
fontWeight: FontWeight.bold
),
),
),
))
],
),
);
}
}
and lastly, the lower area
class LowerArea extends StatefulWidget {
final AnimationController controller;
final Animation<Offset> anime;
bool rep;
LowerArea({Key? key, required this.controller, required this.anime, required this.rep}) : super(key: key);
#override
State<LowerArea> createState() => _LowerAreaState();
}
class _LowerAreaState extends State<LowerArea> {
bool _bigger = true;
bool _fade = true;
void move(){
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.all(1.h),
child: const Text('Tall Frappuccino',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.only(right: 5.h),
child: const Text('Swipe Down',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.all(2.h),
child: const Text('Pickup',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
)
],
),
),
SlideTransition(
position: widget.anime,
child: AnimatedContainer(
// height: 11.h,
width: _bigger ? 35.h: 80.h,
duration: const Duration(milliseconds: 500),
child: Stack(
fit: StackFit.loose,
children: [
Center(child: Image.asset('assets/images/baggie.png')),
Center(
child: Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: GestureDetector(
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
widget.rep = !widget.rep;
print('this is fade $_fade ');
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: _fade? Container(
key: Key('1'),
height: 8.h,
width: 7.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(0.3.h),
child: Icon(
Icons.lock_outline,
color: Colors.white54,
size: 2.5.h,
),
),
Icon(
Icons.arrow_drop_down,
color: Colors.white12,
size: 3.h,
),
],
),
):null,
),
),
),
)
],
),
),
)
],
);
}
use provider pacakges https://pub.dev/packages/provider
Create a class that inherits the functions of ChangeNotifyer to create a flag to control and create a setter.
provider class
class StateController extends ChangeNotifier{
bool _req = false;
bool get req => _req; //getter
setReqValue(){
_req = !_req;
notifyListener();
}
}
Declare the provider class in the main function. You can change the location of the declaration according to Wiget tree, but first declare it in main
Change main.dart
void main(){
runApp(
Multiprovider(
providers: [
ChangeNotifierProvider(create: (_) => StateController()),
],
child: HomePage(),
)
);
}
The UI is updated by notifyListener().
change UI
child: context.watch<StateController>().req == true ? Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
Change State
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
context.read<StateController>().setReqValue();
print('this is fade $_fade ');
});
},
I am too lazy to try understand your code. But if you want to update state of the page after you pop from Navigation to it.
In page you want to update
Navigation.push(context, /* Page that will change something */)
// Future will trigger then you return to this page.
.then((_) => setState(() {}))
I want the dialog to open when a button is clicked, but an error occurs due to BLoC. Previously, there was such an error with the class itself, but I successfully solved it, and in this case, the complexity already arises. I've already tried a couple of options but couldn't solve it. I tried to make the event in onPressed as a separate widget, and the same error did not work either.
home_page
class HomePage extends StatelessWidget {
final todoRepository = TodoRepository();
#override
Widget build(BuildContext context) {
// final TodoBloc todoBloc = context.read<TodoBloc>();
return BlocProvider<TodoBloc>(
create: (context) => TodoBloc(todoRepository),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter Todos'),
),
// floatingActionButton: FloatingActionButton(
// onPressed: () {
// // _addTodo(context);
// final newTodo = Todo(description: 'Todo 1');
// BlocProvider.of<TodoBloc>(context).add(CreateTodos(newTodo));
// },
// child: const Icon(Icons.add),
// ),
body: SingleChildScrollView(
child: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
// ActionButton(),
TodoList(),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add, size: 32, color: Colors.black),
onPressed: () {
// showAddTodoSheet(context);
// showAddTodoSheet(BuildContext context) {
final TodoBloc todoBloc = context.read<TodoBloc>();
final _todoDescriptionFromController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return BlocBuilder<TodoBloc, TodoState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
color: Colors.transparent,
child: Container(
height: 230,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0))),
child: Padding(
padding: const EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller:
_todoDescriptionFromController,
textInputAction:
TextInputAction.newline,
maxLines: 4,
style: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'I have to...',
labelText: 'New Todo',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500)),
validator: (value) {
if (value!.isEmpty) {
return 'Empty description!';
}
return value.contains('')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: const EdgeInsets.only(
left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: const Icon(
Icons.save,
size: 22,
color: Colors.white,
),
onPressed: () {
final newTodo = Todo(
description:
_todoDescriptionFromController
.value.text);
if (newTodo
.description.isNotEmpty) {
todoBloc
.add(CreateTodos(newTodo));
Navigator.pop(context);
}
},
),
),
)
],
),
],
),
),
),
),
);
});
});
},
),
));
}
You have to use MultiBlocProvider before MaterialApp.
just do like that
#override
Widget build(BuildContext context) => MultiBlocProvider(
providers: [
BlocProvider(create: (_) => TodoBloc()),
],
child: MaterialApp()
);
I am building a food cart application and have a query on the menu item page.
My menu items page consists of a list view, which shows each item with description along with an option to specify the quantity of items( 1, 2, 3....)
Now I am able to increment or decrement item count for a single item, but not sure how to do that in a list view at a particular index where the user clicks.
Could some one please help me on this?
The code which I have done till now is as below :
FutureBuilder(
future: getItemList(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
var itemList = snapshot.data;
int itemlength = itemList.length;
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
margin: EdgeInsets.only(left: 10, right: 10, top: 10),
decoration: BoxDecoration(
color: Colors.white70,
border: Border.all(width: 2.0, color: Colors.blueGrey),
borderRadius: BorderRadius.circular(6.0),
boxShadow: [
BoxShadow(
color: Color(0xFF6078ea).withOpacity(.3),
offset: Offset(0.0, 8.0),
blurRadius: 8.0),
],
),
child: Row(
children: < Widget > [
Container(
width: 75,
child: ClipRRect(
borderRadius: new BorderRadius.circular(10.0),
child: Image(
image: AssetImage(
'assets/images/loginbg3.jpg'),
fit: BoxFit.cover,
),
),
margin: EdgeInsets.only(left: 10),
),
Expanded(
child: Container(
child: Column(
children: < Widget > [
Row(
children: < Widget > [
Expanded(child: Container(margin: EdgeInsets.only(left: 10, top: 15, bottom: 5), child: Text(itemList[index]['itemname'], style: kClickableTextStyle, ))),
Container(color: Colors.white, width: 25, margin: EdgeInsets.only(left: 10, top: 15, bottom: 5), child: Image.asset(_itemtype)),
],
),
Row(
children: < Widget > [
Container(margin: EdgeInsets.only(left: 10, top: 15, bottom: 5), child: Text('Price : ', style: kListTitleTextStyle, )),
Container(margin: EdgeInsets.only(left: 10, top: 15, bottom: 5), child: Text(numberofitems.toString() + ' \u20B9 ', style: kListTitleTextStyle, )),
],
),
],
),
),
),
Container(
margin: EdgeInsets.only(left: 10),
child: Row(
children: < Widget > [
InkWell(
onTap: onClickDelete,
child: Container(
width: 30, child: Icon(
Icons.remove_circle_outline,
color: Colors.green,
size: 30,
), ),
),
Container(
width: 30,
child: Center(child: Text(_count.toString(), style: TextStyle(fontSize: 25), )),
),
InkWell(
onTap: onClickAdd,
child: Container(
margin: EdgeInsets.only(right: 5),
width: 30, child: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
), ),
),
],
),
),
],
),
);
},
itemCount: itemList == null ? 0 : itemList.length,
);
})
You can copy paste run full code below
Step 1: create Item class
Step 2: change future builder call
Step 3: deal with Inkwell onTap
working demo
code snippet
Future callback;
#override
void initState() {
callback = _getItemList();
super.initState();
}
#override
Widget build(BuildContext context) {
var futureBuilder = FutureBuilder(
future: callback,
...
InkWell(
onTap: () {
if (itemList[index].numberofitems > 0) {
setState(() {
itemList[index].numberofitems =
itemList[index].numberofitems - 1;
});
}
},
...
InkWell(
onTap: () {
setState(() {
itemList[index].numberofitems =
itemList[index].numberofitems + 1;
print(
' ${itemList[index].itemname.toString()} ${itemList[index].numberofitems.toString()}');
});
},
full code
import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class Item {
String itemname;
String itemtype;
int numberofitems;
Item({this.itemname, this.itemtype, this.numberofitems});
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future callback;
#override
void initState() {
callback = _getItemList();
super.initState();
}
#override
Widget build(BuildContext context) {
var futureBuilder = FutureBuilder(
future: callback,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return createListView(context, snapshot);
}
},
);
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: futureBuilder,
);
}
Future<List<Item>> _getItemList() async {
var values = List<Item>();
values.add(
Item(itemname: "A", itemtype: "assets/images/a.jpg", numberofitems: 0));
values.add(
Item(itemname: "B", itemtype: "assets/images/a.jpg", numberofitems: 1));
values.add(
Item(itemname: "C", itemtype: "assets/images/a.jpg", numberofitems: 2));
//throw Exception("Danger Will Robinson!!!");
await Future.delayed( Duration(seconds: 2));
return values;
}
Widget createListView(BuildContext context, AsyncSnapshot snapshot) {
List<Item> itemList = snapshot.data;
return ListView.builder(
itemCount: itemList.length,
itemBuilder: (BuildContext context, int index) {
{
return Container(
height: 100,
margin: EdgeInsets.only(left: 10, right: 10, top: 10),
decoration: BoxDecoration(
color: Colors.white70,
border: Border.all(width: 2.0, color: Colors.blueGrey),
borderRadius: BorderRadius.circular(6.0),
boxShadow: [
BoxShadow(
color: Color(0xFF6078ea).withOpacity(.3),
offset: Offset(0.0, 8.0),
blurRadius: 8.0),
],
),
child: Row(
children: <Widget>[
Container(
width: 75,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image(
image: AssetImage('assets/images/a.jpg'),
fit: BoxFit.cover,
),
),
margin: EdgeInsets.only(left: 10),
),
Expanded(
child: Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.only(
left: 10, top: 15, bottom: 5),
child: Text(
itemList[index].itemname,
))),
Container(
color: Colors.white,
width: 25,
margin: EdgeInsets.only(
left: 10, top: 15, bottom: 5),
child: Image.asset(itemList[index].itemtype)),
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 10, top: 15, bottom: 5),
child: Text(
'Price : ',
)),
Container(
margin: EdgeInsets.only(
left: 10, top: 15, bottom: 5),
child: Text(
itemList[index].numberofitems.toString() +
' \u20B9 ',
)),
],
),
],
),
),
),
Container(
margin: EdgeInsets.only(left: 10),
child: Row(
children: <Widget>[
InkWell(
onTap: () {
if (itemList[index].numberofitems > 0) {
setState(() {
itemList[index].numberofitems =
itemList[index].numberofitems - 1;
});
}
},
child: Container(
width: 30,
child: Icon(
Icons.remove_circle_outline,
color: Colors.green,
size: 30,
),
),
),
Container(
width: 30,
child: Center(
child: Text(
itemList[index].numberofitems.toString(),
style: TextStyle(fontSize: 25),
)),
),
InkWell(
onTap: () {
setState(() {
itemList[index].numberofitems =
itemList[index].numberofitems + 1;
print(
' ${itemList[index].itemname.toString()} ${itemList[index].numberofitems.toString()}');
});
},
child: Container(
margin: EdgeInsets.only(right: 5),
width: 30,
child: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
),
),
),
],
),
),
],
),
);
}
;
});
}
}
you need to create list of objects for your Item like.
class Item{
String name;
String description;
int quantity;
double price;
}
and on tap of item, get index of clicked item with new incremented/decremented value and update your list of objects inside setState().
You need to wrap your itemBuilder item, i.e the Container with a GestureDetector and then call the onTap() method inside that detector.
Before that, you'll have to convert your item into a stateful widget with a variable to maintain the count of quantity.
I'm new to flutter. I need help in the progress indicator. I have the code of my buttons with strings, I need to make the transition to "FirstScreen" through the indicator. So that when I click on the "Login" button, the progress indicator will pop up for 3 seconds and then disappear.
Edit:
I was able to reach this result:
class LoginScreen extends StatefulWidget {
#override
State createState() => new _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String _email = '';
String _password = '';
bool _load = false;
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.only(top: 150),
child: Column(
children: <Widget>[
Image.asset(
'img/img_login.png',
width: 50,
height: 50,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 0.0),
child: TextField(
decoration: InputDecoration(
labelText: 'Email',
icon: Padding(
padding: EdgeInsets.only(top: 15.0),
child: Icon(Icons.email),
)),
),
),
SizedBox(
height: 15.0,
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 0.0),
child: PasswordField(
helperText: 'No more than 8 characters',
labelText: 'Password',
)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding:
EdgeInsets.only(left: 20.0, right: 5.0, top: 20.0),
child:
FlatButton(
onPressed: ()
{
setState((){
_load = true;
});
},
// GestureDetector(
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => FirstScreen()));
// },
child: Container(
alignment: Alignment.center,
height: 45.0,
decoration: BoxDecoration(
color: Color(0xFF1976D2),
borderRadius: BorderRadius.circular(9.0)),
child: Text('Login',
style: TextStyle(
fontSize: 20.0, color: Colors.white))),
),
),
),
Expanded(
child: Padding(
padding:
EdgeInsets.only(left: 10.0, right: 20.0, top: 20.0),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen()));
},
child: Container(
alignment: Alignment.center,
height: 45.0,
decoration: BoxDecoration(
color: Color(0xFFD32F2F),
borderRadius: BorderRadius.circular(9.0)),
child: Text('Register',
style: TextStyle(
fontSize: 17.0, color: Colors.white))),
),
),
)
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 18.0),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen()));
},
child: Text('Forgot password?',
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
))
],
))
],
)),
);
}
}
You can do This using material.dart library
import 'package:flutter/material.dart';
class LoadingDialog extends StatefulWidget{
LoadingDialogState state;
bool isShowing(){
return state!=null&&state.mounted;
}
#override
createState()=> state=LoadingDialogState();
}
class LoadingDialogState extends State<LoadingDialog>{
#override
Widget build(BuildContext context) {
return Align(alignment: Alignment.center,
child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.lightBlue
) ,),);
}
}
Add your LoadingDialog class
you can hide or show your loading dialog using showLoadingDialog() and hideDialog()
class LoginScreen extends StatefulWidget {
#override
State createState() => new _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String _email = '';
String _password = '';
bool _load = false;
LoadingDialog loadingDialog;
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.only(top: 150),
child: Column(
children: <Widget>[
Image.asset(
'img/img_login.png',
width: 50,
height: 50,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 0.0),
child: TextField(
decoration: InputDecoration(
labelText: 'Email',
icon: Padding(
padding: EdgeInsets.only(top: 15.0),
child: Icon(Icons.email),
)),
),
),
SizedBox(
height: 15.0,
),
/* Padding(
padding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 0.0),
child: Text(
helperText: 'No more than 8 characters',
labelText: 'Password',
))*/
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding:
EdgeInsets.only(left: 20.0, right: 5.0, top: 20.0),
child:
FlatButton(
onPressed: ()
{
setState((){
showLoadingDialog();
});
},
// GestureDetector(
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => FirstScreen()));
// },
child: Container(
alignment: Alignment.center,
height: 45.0,
decoration: BoxDecoration(
color: Color(0xFF1976D2),
borderRadius: BorderRadius.circular(9.0)),
child: Text('Login',
style: TextStyle(
fontSize: 20.0, color: Colors.white))),
),
),
),
Expanded(
child: Padding(
padding:
EdgeInsets.only(left: 10.0, right: 20.0, top: 20.0),
child: GestureDetector(
onTap: () {
// Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
},
child: Container(
alignment: Alignment.center,
height: 45.0,
decoration: BoxDecoration(
color: Color(0xFFD32F2F),
borderRadius: BorderRadius.circular(9.0)),
child: Text('Register',
style: TextStyle(
fontSize: 17.0, color: Colors.white))),
),
),
)
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 18.0),
child: GestureDetector(
onTap: () {
// Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
},
child: Text('Forgot password?',
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
))
],
))
],
)),
);
}
void showLoadingDialog() async {
await new Future.delayed(new Duration(milliseconds: 30));
await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) =>
loadingDialog = loadingDialog ?? LoadingDialog());
//builder: (BuildContext context) => WillPopScope(child:loadingDialog = LoadingDialog() , onWillPop: () async => false,));
}
Future hideDialog() {
if (loadingDialog != null && loadingDialog.isShowing()) {
Navigator.of(context).pop();
loadingDialog = null;
}
}
}
Try to use progress hud. It's work for me. Here is the link.
https://pub.dartlang.org/packages/progress_hud
Hi I have designed a screen in flutter. I have AlertDialog on which I want to close the dialog and screen on pressing. Right now AlertDialog dismiss on press but screen is not closing.
Does anyone know how to do this ?
class ForgotPasswordScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return ForgotPasswordScreenState();
}
}
class ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
var emailController = new TextEditingController();
var authHandler = new Auth();
bool isLoading = false;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Colors.white,
),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
children: <Widget>[
new Expanded(
child: isLoading
? Center(child: CircularProgressIndicator())
: new Container()),
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text(
"EMAIL",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.redAccent,
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin:
const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.redAccent,
width: 0.5,
style: BorderStyle.solid),
),
),
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextField(
controller: emailController,
textAlign: TextAlign.left,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'PLEASE ENTER YOUR EMAIL',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
Divider(
height: 24.0,
),
new Container(
width: MediaQuery.of(context).size.width,
margin:
const EdgeInsets.only(left: 30.0, right: 30.0, top: 20.0),
alignment: Alignment.center,
child: new Row(
children: <Widget>[
new Expanded(
child: new FlatButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
color: Colors.redAccent,
onPressed: () {
setState(() {
isLoading = true;
});
authHandler
.sendPasswordResetEmail(emailController.text)
.then((void nothing) {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
content: new Text(
"Password reset email has been sent."),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
setState(() {
isLoading = false;
});
}).catchError((e) => print(e));
},
child: new Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 20.0,
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: Text(
"FORGOT PASSWORD",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
],
),
),
],
)));
}
}
Ideally, you'll want to call pop more than once. One for the modal, another for the actual route.
There are a few ways to achieve this. But ideally you'll want to await the close of the dialog before triggering another close:
foo() async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
actions: [
new FlatButton(
child: new Text("OK"),
onPressed: () => Navigator.pop(context),
),
],
),
);
Navigator.pop(context);
}
This way, both the route and the modal can handle their close however they like.
This is how i did with mine
bool _logout = false;
and then at the start of build Widget
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
_onBackPressed(context);
return _logout;
},
child: Container(),
);
}
and the method _onBackPressed returns a custom dialog Class like so
void _onBackPressed(BuildContext c) async {
await showDialog(
barrierColor: CustomColors.darkGrey.withOpacity(0.8),
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return CustomDialogBox(
title: 'Logout',
description: 'Are you sure you want to logout?',
rightButtonText: 'Yes',
onPClick: () {
_logout = true;
if (_logout == true) {
Get.back();
}
},
onNClick: () {
_logout = false;
Get.back();
},
);
});
if (_logout == true) {
Get.back();
}
}
and my custom Dialog class is here
class CustomDialogBox extends StatefulWidget {
final String? title, description, leftButtonText, rightButtonText;
final VoidCallback? onPClick, onNClick;
final Image? image;
const CustomDialogBox({
Key? key,
this.title,
this.description,
this.leftButtonText,
this.rightButtonText,
this.image,
this.onPClick,
this.onNClick,
}) : super(key: key);
#override
_CustomDialogBoxState createState() =>
// ignore: no_logic_in_create_state
_CustomDialogBoxState(onPClick!, onNClick!);
}
class _CustomDialogBoxState extends State<CustomDialogBox> {
final VoidCallback onPClick, onNClick;
_CustomDialogBoxState(this.onPClick, this.onNClick);
#override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Dimensions.BORDER_RADIUS_4),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Container(
//height: 200,
padding: const EdgeInsets.only(
left: 10,
right: 0,
bottom: 10,
),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: CustomColors.darkGrey,
offset: const Offset(0, 30),
blurRadius: 20,
),
]),
child: Wrap(children: <Widget>[
dialogBody(context),
]),
),
);
}
Widget dialogBody(context) {
return Column(children: [
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Image.asset(
Images.LOGO,
height: MediaQuery.of(context).size.height * 0.035,
),
IconButton(
//padding: const EdgeInsets.all(0),
onPressed: () {
Get.back();
},
icon: const CircleAvatar(
radius: 12.5,
child: Icon(
Icons.close,
color: Colors.white,
),
backgroundColor: Colors.red,
),
),
]),
Padding(
padding: const EdgeInsets.only(
right: 10,
),
child: Column(children: [
//----//
customText(
text: widget.title ?? '',
fontFamily: 'black',
fontSize: 16,
),
//----//
const Space(0, 0.01),
//----//
customText(
text: widget.description ?? '',
fontSize: 14,
),
]),
),
//----//
const Space(0, 0.03),
//----//
Padding(
padding: const EdgeInsets.only(
right: 10,
),
child:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
//----//
raisedButton(
text: widget.leftButtonText ?? 'Cancel',
fontFamily: 'roman',
height: 35,
width: 105,
buttonColor: CustomColors.red,
onClick: () {
return onNClick();
},
context: context,
),
//----//
raisedButton(
text: widget.rightButtonText ?? 'Okay',
fontFamily: 'roman',
height: 35,
width: 105,
buttonColor: CustomColors.green,
onClick: () {
return onPClick();
},
context: context,
),
//----//
]),
),
]);
}
}
the buttons and texts are custom so feel free to change them. and where you see Get.back(); is GetX code.. you can replace with Navigator.of(context).pop();
try this
showPop() async {
await showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
actions: [
new FlatButton(
child: new Text("Close"),
onPressed: () => Navigator.pop(context),
),
],
),
);
}