How to return data from custom function which uses bottom sheet in flutter - flutter

I made a custom function which opens modal bottom sheet in flutter. Now, I want to get some data back from the sheet to my previous page. How should I do it? I tried to make function's return type as Future<FilterDataModel> and Future, but it's not working. I want that whenever the user clicks on cancel, it should return false and when he presses apply, i should get true with the data.
Here is what I tried -
Future<FilterDataModel> showFilterBottomSheet<T>(
{required BuildContext context}) async {
Some code ...........
FilterDataModel filterData = FilterDataModel();
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
String val = "One";
return StatefulBuilder(
builder: (context, StateSetter setState) {
return Wrap(
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: getProportionateScreenHeight(20),
horizontal: getProportionateScreenWidth(16),
),
child: Column(
..............
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.pop(context, [false, filterData]);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white),
),
padding: EdgeInsets.symmetric(
vertical: getProportionateScreenHeight(16),
),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: primaryText2,
fontSize: 16,
),
),
),
),
),
),
SizedBox(
width: getProportionateScreenWidth(20),
),
Expanded(
child: InkWell(
onTap: () {
Navigator.pop(context, [true, filterData]);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.black38),
),
padding: EdgeInsets.symmetric(
vertical: getProportionateScreenHeight(16),
),
child: Center(
child: Text(
'Apply',
style: TextStyle(
color: primaryOrange,
fontSize: 16,
),
),
),
),
),
),
],
),
],
),
),
],
);
},
);
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
).then((value) {
debugPrint("Coming data");
debugPrint(filterData.academicYear.toString());
return filterData;
});
return filterData;
}
And how I am calling it -
onPressed: () async {
FilterDataModel f = await showFilterBottomSheet(
context: context,
);
print("Here - ${f.academicYear}");
},
I also tried to do it like this -
onPressed: () async {
await showFilterBottomSheet(
context: context,
).then((value) {
print("Inside then");
print(value[0]);
print(value[1].toString());
});
print("Here - ${f.academicYear}");
},
But it's not working.

You need to await your bottom sheet, to get the result that was returned from the Navigator.pop(context, value). so it will be like.
final res = await showModalBottomSheet(context, ....);
/// this is to make sure that the Navigator.pop(context) from the bottom sheet did not return a null.
if (res != null) {
FilterDataModel filterData = FilterDataModel();
return filterData;
} else {
return anything;
}

looks like when you pop navigator you return List< dynamic> (boolean + filterDataModel)
so the scheme is:
final result = await showModalBottomSheet<dynamic>(){
...
...
return YourWidget(
...
onTap: ()=> Navigator.of(context).pop([false, filterDataModel])
...
)
}
final boolResult = result[0] as boolean;
final dataResult = result[1] as FilterDataModel;`
take a note that if modal is dismissible then return will be null in case it is dismissed without returned value and you will have to handle this case also

Related

TextFormField i cant type anything

im facing a problem with TextFormField which when i type anything on the keyboard it doesn't appear in the text field, im trying doing a chat application with BLOC. Note : when i remove this line...emit(SocialGetMessageSuccessState()); i can type normaly and the problem is gone but 2 bugs appear..first one is that when i enter chat with someone the older messages don't appear unless i send him a new message...second one is when i enter a different chat with someone else the messages of the first user appears to the other user and to solve this one i should exit the chat page and enter again to see the messages of the wanted user that i currenlty chatting him/
the problem is when i type any character on the keyboard it won't print in the text field and it gives me this :
getTextBeforeCursor on inactive InputConnection
getSelectedText on inactive InputConnection
getTextAfterCursor on inactive InputConnection
here is my code
List<SocialUserModel> users =[];
void getUsers()
{
if(users.length ==0)
FirebaseFirestore.instance.collection('users').get().then((value)
{
value.docs.forEach((element)
{
if(element.data()['uId'] != userModel!.uId)
users.add(SocialUserModel.fromJson(element.data()));
});
emit(SocialGetAllUsersSuccessState());
})
.catchError((error){
emit(SocialGetAllUsersErrorState(error.toString()));
});
}
void sendMessage({
required String receiverId,
required String dateTime,
required String text,
})
{
MessageModel model = MessageModel(
text: text,
senderId: userModel!.uId,
receiverId: receiverId,
dateTime: dateTime,
);
// set my chats
FirebaseFirestore.instance
.collection('users')
.doc(userModel!.uId)
.collection('chats')
.doc(receiverId)
.collection('messages')
.add(model.toMap())
.then((value){
emit(SocialSendMessageSuccessState());
})
.catchError((error)
{
emit(SocialSendMessageErrorState());
});
// set receiver chats
FirebaseFirestore.instance
.collection('users')
.doc(receiverId)
.collection('chats')
.doc(userModel!.uId)
.collection('messages')
.add(model.toMap())
.then((value){
emit(SocialSendMessageSuccessState());
})
.catchError((error)
{
emit(SocialSendMessageErrorState());
});
}
List<MessageModel> messages = [];
void getMessages({
required String receiverId,
})
{
FirebaseFirestore.instance
.collection('users')
.doc(userModel!.uId!)
.collection('chats')
.doc(receiverId)
.collection('messages')
.orderBy('dateTime')
.snapshots()
.listen((event)
{
messages=[];
event.docs.forEach((element)
{
messages.add(MessageModel.fromJson(element.data()));
});
emit(SocialGetMessageSuccessState());
});
}
//getUser screen
class ChatsScreen extends StatelessWidget {
const ChatsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocConsumer<SocialCubit,SocialStates>(
listener: (context,state){},
builder: (context,state)
{
return ConditionalBuilder(
condition: SocialCubit.get(context).users.length > 0,
builder: (context) => ListView.separated(
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) => buildChatItem(context,SocialCubit.get(context).users[index]),
separatorBuilder: (context, index) => SizedBox(height:1),
itemCount: SocialCubit.get(context).users.length
),
fallback: (context) => Center(child: CircularProgressIndicator()),
);
}
);
}
Widget buildChatItem(BuildContext context,SocialUserModel model) => InkWell(
onTap: ()
{
Navigator.push(context, MaterialPageRoute(builder: (context) => ChatDetailsScreen(userModel: model)));
},
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Row(
children:
[
CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'${model.image}'
),
),
SizedBox(width: 15,),
Row(
children: [
Text(
'${model.name}',
),
],
),
],
),
),
);
}
//ChatScreen
class ChatDetailsScreen extends StatelessWidget {
SocialUserModel? userModel;
final messageController = TextEditingController();
ChatDetailsScreen({this.userModel}) {}
#override
Widget build (context) {
return Builder(
builder: (context)
{
SocialCubit.get(context).getMessages(receiverId: userModel!.uId!);
return BlocConsumer<SocialCubit,SocialStates>(
listener: (context,state) {},
builder: (context,state)
{
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
title: Row(
children: [
CircleAvatar(
radius: 20,
backgroundImage: NetworkImage(userModel!.image!),
),
SizedBox(
width: 15,
),
Text(
userModel!.name!,
)
],
),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: ListView.separated(
physics: BouncingScrollPhysics(),
itemBuilder: (context,index)
{
var message = SocialCubit.get(context).messages[index];
if(SocialCubit.get(context).userModel!.uId == message.senderId)
return buildMyMessage(message);
return buildMessage(message);
},
separatorBuilder: (context,index) => SizedBox(height: 7,),
itemCount: SocialCubit.get(context).messages.length
),
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey[300]!,
width: 1,
),
borderRadius: BorderRadius.circular(15)
),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Row(
children:
[
SizedBox(width: 10,),
Expanded(
child: TextFormField(
controller: messageController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'type your message here...',
),
),
),
Container(
height: 50,
color: Colors.blue,
child: MaterialButton(onPressed:()
{
SocialCubit.get(context).sendMessage(
receiverId: userModel!.uId!,
dateTime: DateTime.now().toString(),
text: messageController.text,
);
},
minWidth: 1,
child: Icon(
IconBroken.Send,
size: 16,
color: Colors.white,
),),
)
],
),
)
],
),
),
);
}
);
}
);
}
Widget buildMessage(MessageModel model) => Align(
alignment: AlignmentDirectional.centerStart,
child: Container(
decoration:BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadiusDirectional.only(
bottomEnd: Radius.circular(10),
topEnd: Radius.circular(10),
topStart: Radius.circular(10),
)
),
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
child: Text(
model.text!,
),
),
);
Widget buildMyMessage(MessageModel model) => Align(
alignment: AlignmentDirectional.centerEnd,
child: Container(
decoration:BoxDecoration(
color: Colors.blue.withOpacity(.2),
borderRadius: BorderRadiusDirectional.only(
bottomStart: Radius.circular(10),
topEnd: Radius.circular(10),
topStart: Radius.circular(10),
)
),
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
child: Text(
model.text!,
),
),
);
}
sory if i can't explain..still newbie here
i solved the problem
i converted the stateless widget into stateful

How to get back a value from a customly created widget in Flutter

I am showing a showModalBottomSheet using a function. I want that as soon as it closes, value of a variable should change. I wanted to change value of two variables, but I am not able to change for even one. Please help me with this. I tried to make my own onChanged and also tried to return the value using function, but nothing happens.
This is the function, please scroll to the last of it and check out the onTap function and return.
String showChapterSelectionSheet(
BuildContext context,
List<ChapterModel> chapter_list,
String chapter_name,
final Function(String) onChapterChanged) {
String retValue = chapter_name;
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
elevation: 0,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
),
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context,
StateSetter setState /*You can rename this!*/) {
return makeDismissible(
context,
child: DraggableScrollableSheet(
initialChildSize: 0.81,
minChildSize: 0.5,
maxChildSize: 0.81,
builder: (_, controller) => Container(
padding: EdgeInsets.all(getProportionateScreenWidth(25)),
height: getProportionateScreenWidth(600),
decoration: BoxDecoration(
color: backgroundColor2,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(
top: getProportionateScreenHeight(32),
bottom: getProportionateScreenHeight(16)),
child: Text(
AppLocalizations.of(context)!.chapters,
style: Theme.of(context)
.textTheme
.headline2!
.apply(color: Colors.white),
),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
controller: controller,
itemCount: chapter_list.length,
itemBuilder: (_, index) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.only(
top: getProportionateScreenHeight(8)),
child: Card(
child: Container(
height: getProportionateScreenHeight(56),
width: getProportionateScreenWidth(341),
decoration: BoxDecoration(
border: Border.all(color: cardColor),
color: chapter_list[index].chapter_name! ==
chapter_name
? backgroundColor
: cardColor,
),
child: Padding(
padding: EdgeInsets.all(0),
child: Center(
child: Row(
children: [
Container(
width:
getProportionateScreenWidth(
32),
child: chapter_list[index]
.chapter_name! ==
chapter_name
? Icon(
Icons.check,
color: brandYellow,
)
: SizedBox()),
Text(
"Chapter ${chapter_list[index].position!}: ",
style: Theme.of(context)
.textTheme
.bodyText2!
.apply(color: brandYellow),
),
Expanded(
child: Text(
chapter_list[index]
.chapter_name!,
style: Theme.of(context)
.textTheme
.bodyText2!
.apply(
color: chapter_list[
index]
.chapter_name! ==
chapter_name
? tertiaryTextColor
: primaryTextColor)),
),
],
),
),
),
),
),
),
onTap: () {
onChapterChanged(chapter_list[index].chapter_name!);
setState(() {
retValue = chapter_list[index].chapter_name!;
});
Navigator.pop(context);
},
);
},
),
),
],
),
),
),
);
},
);
},
);
return retValue;
}
And I am accessing it here -
return InkWell(
onTap: () async {
if(dataList.isNotEmpty) {
chapterName.value = showChapterSelectionSheet(
context,dataList,chapterName.value,(val) {
setState(() {
chapterName.value = val;
print("Val is - $val");
});
}
);
}
},
child: .....
);
In the above InkWell, the print statement is working fine but value is not changing.
And I want to update and use the value here -
child: ValueListenableBuilder(
valueListenable: chapterName,
builder: (context, String val, Widget? child) {
return Text(
val,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
);
},
),
It is possible you are just missing await before await showModalBottomSheet(..).
You can follow this simplified snippet.
class BVChange extends StatefulWidget {
const BVChange({Key? key}) : super(key: key);
#override
State<BVChange> createState() => _BVChangeState();
}
class _BVChangeState extends State<BVChange> {
String var1 = "Old", var2 = "old1";
Future<String> _showDialog(String v) async {
double _sliderValue = 0.0;
await showModalBottomSheet(
context: context,
builder: (_) {
return StatefulBuilder(
builder: (context, sbSate) => Column(
children: [
Text(_sliderValue.toString()),
Slider(
value: _sliderValue,
onChanged: (sval) {
sbSate(() {
_sliderValue = sval;
});
}),
],
),
);
});
return _sliderValue.toString();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
GestureDetector(
onTap: () async {
final data = await _showDialog(var1);
setState(() {
var1 = data;
});
},
child: Text("var1 : $var1")),
GestureDetector(
onTap: () async {
final data = await _showDialog(var2);
setState(() {
var2 = data;
});
},
child: Text("var 2 : $var2"),
),
],
),
);
}
}

Flutter General dialog box - set state not working

I have an issue with my General Dialog Box. I would like to display a star. Then I would like to change it state when the star is taped and replace the icon by a yellow Star.
But is does not work. The Dialog Box is not refreshed so the icon is not changing. Please, can you look at the source code below and point me into the right direction please?
Many thanks.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:date_time_picker/date_time_picker.dart';
import 'package:gtd_official_sharped_focused/snackbar.dart';
String _isImportantInboxTask ;
String _isUrgentInboxTask ;
String inboxTaskDisplayed;
String isImportant = "false" ;
String isUrgent = "false" ;
String myProjectName ;
var taskSelectedID;
//---------------
//String _initialValue;
//_-----------------
var documentID;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
class Inbox extends StatefulWidget {
Inbox({Key key}) : super(key: key);
#override
_InboxState createState() => _InboxState();
}
class _InboxState extends State<Inbox> {
GlobalKey<FormState> _captureFormKey = GlobalKey<FormState>();
bool isOn = true;
#override
Widget build(BuildContext context) {
void showAddNote() {
TextEditingController _noteField = new TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return CustomAlertDialog(
content: Container(
width: MediaQuery.of(context).size.width / 1.3,
height: MediaQuery.of(context).size.height / 4,
child: Column(
children: [
TextField(
controller: _noteField,
maxLines: 4,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.black, width: 1.0),
),
),
),
SizedBox(height: 10),
Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(25.0),
color: Colors.white,
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width / 1.5,
onPressed: () {
Navigator.of(context).pop();
CollectionReference users = FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('allTasks');
users
.add({'task_Name': _noteField.text,'task_Status': 'Inbox' })
.then((value) => print("User Document Added"))
.catchError((error) =>
print("Failed to add user: $error"));
},
padding: EdgeInsets.fromLTRB(10.0, 15.0, 10.0, 15.0),
child: Text(
'Add Note',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
);
});
}
return Scaffold(
appBar: new AppBar(
title: new Text('Inbox Page'),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.add_circle_outline,
color: Colors.white,
),
onPressed: () {
showAddNote();
// do something
},
),
],
),
drawer: MyMenu(),
backgroundColor: Colors.white,
body: Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: MediaQuery.of(context).size.height / 1.4,
width: MediaQuery.of(context).size.width,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('allTasks')
.where('task_Status', isEqualTo: 'Inbox')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView(
children: snapshot.data.docs.map((document) {
return Wrap(
children: [Card(
child: SwipeActionCell(
key: ObjectKey(document.data()['task_Name']),
actions: <SwipeAction>[
SwipeAction(
title: "delete",
onTap: (CompletionHandler handler) {
CollectionReference users = FirebaseFirestore
.instance
.collection('Users')
.doc(
FirebaseAuth.instance.currentUser.uid)
.collection('allTasks');
users
.doc(document.id)
.delete()
.then((value) => print("Note Deleted"))
.catchError((error) => print(
"Failed to delete Task: $error"));
},
color: Colors.red),
],
child: Padding(
padding: const EdgeInsets.all(0.0),
child: ListTile(
leading: ConstrainedBox(
constraints: BoxConstraints(
minWidth: leadingIconMinSize,
minHeight: leadingIconMinSize,
maxWidth: leadingIconMaxSize,
maxHeight: leadingIconMaxSize,
),
child: Image.asset('assets/icons/inbox.png'),
),
title: GestureDetector(
child: Text(
//'task_Name' correspond au nom du champ dans la table
document.data()['task_Name'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
// Pour editer task
onDoubleTap: (){
taskSelectedID = FirebaseFirestore
.instance
.collection('Users')
.doc(
FirebaseAuth.instance.currentUser.uid)
.collection('allTasks')
.doc(document.id);
//Dialog
return showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context)
.modalBarrierDismissLabel,
barrierColor: Colors.black45,
transitionDuration: const Duration(milliseconds: 20),
pageBuilder: (BuildContext buildContext,
Animation animation,
Animation secondaryAnimation) {
return Scaffold(
appBar: AppBar(
title: Text ('Edit Task'),
leading: InkWell(
child: Icon(Icons.close),
onTap:(){Navigator.of(context).pop();}
),
actions: [Padding(
padding: const EdgeInsets.fromLTRB(0, 0,16.0,0),
child: InkWell(
child: Icon(Icons.save),
onTap: () {
final loFormInbox = _captureFormKey
.currentState;
if (loFormInbox.validate()) {
loFormInbox.save();
CollectionReference users = FirebaseFirestore
.instance
.collection(
'Users')
.doc(FirebaseAuth
.instance
.currentUser.uid)
.collection(
'allTasks');
users
.add({
'task_Name': _valueTaskNameSaved,
})
.then((value) =>
print(
"Task Created"))
.catchError((
error) =>
print(
"Failed to add task: $error"));
showSimpleFlushbar(
context,
'Task Saved',
_valueTaskNameSaved,
Icons
.mode_comment);
loFormInbox.reset();
isImportant = 'false';
isUrgent = 'false';
}
}
),
)],
),
body: Center(
child: Container(
width: MediaQuery.of(context).size.width - 10,
height: MediaQuery.of(context).size.height - 80,
padding: EdgeInsets.all(20),
color: Colors.white,
child: Column(
children: [
Theme(
data: ThemeData(
inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none,
)
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 15.0, 1.0),
child: TextFormField(
initialValue: document.data()['task_Name'],
decoration: InputDecoration(hintText: "Task Name"),
maxLength: 70,
maxLines: 2,
onChanged: (valProjectName) => setState(() => _valueTaskNameChanged = valProjectName),
validator: (valProjectName) {
setState(() => _valueTaskNameToValidate = valProjectName);
return valProjectName.isEmpty? "Task name cannot be empty" : null;
},
onSaved: (valProjectName) => setState(() => _valueTaskNameSaved = valProjectName),
),
)),
//Test Energy et Time / Important /urgent
Material(
child:
Container(
// color: Colors.red,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:[
//Important
FlatButton(
child:
InkWell(
child: Container(
// color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
isImportant =="true" ? Icon(Icons.star,color: Colors.orange,) :
Icon(Icons.star_border, color: Colors.grey,),
// Icon(Icons.battery_charging_full),
Text('Important'),
],
)
),
onTap: () {
setState(() {
if (isImportant=='true'){
isImportant = 'false';}
else
{isImportant= 'true';
}
});
},
),
),
RaisedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Close",
style: TextStyle(color: Colors.white),
),
color: const Color(0xFF1BC0C5),
)
//++++++++++++++++
],
),
),
),
);
});
},
),
),
),
),
),
),
]
);
}).toList(),
);
}),
),
],
),
bottomNavigationBar: MyBottomAppBar(), //PersistentBottomNavBar(),
);
}
}
#override
Widget build(BuildContext context){
return _widget();
}
}
Thanks to your solution, I am able to do what I was willing to do. But now, I have an other issue. In the version 1 of my code, I am using this code
Theme(
data: ThemeData(
inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none,
)
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 15.0, 1.0),
child: TextFormField(
initialValue: document.data()['task_Name'],
decoration: InputDecoration(hintText: "Task Name"),
maxLength: 70,
maxLines: 2,
onChanged: (valProjectName) => setState(() => _valueTaskNameChanged = valProjectName),
validator: (valProjectName) {
setState(() => _valueTaskNameToValidate = valProjectName);
return valProjectName.isEmpty? "Task name cannot be empty" : null;
},
onSaved: (valProjectName) => setState(() => _valueTaskNameSaved = valProjectName),
),
)),
This part was working well. But after the modifications, I am getting an error. The error is about document.
Undefined name 'document'. Try correcting the name to one that is defined, or defining the name.
Please, can you help me with this so I can finalize this page. Thank you
So you want to change the color of icon on clicking it inside dialogBox,
but unfortunately you are using stateless widget Scaffold in return of showGeneralDialog builder so one thing that can possibly help is to make a separate StateFull Widget RatingDialogBox and use that in the builder.
Also instead of InkWell you can use IconButton
I will suggest you to use this package it is great
flutter_rating_bar
also feel free to comment is this doesn't satisfy your need

Flutter: Change ListTile based on Firestore query

I'm trying to configure the appearance of a list tile based on a firestore query.
So I have a set of list tiles as such:
What I want to achieve, when this page is loaded, the left side of the tile marks if a user has completed that particular lesson. So an 'x' means he has not but a 'tick' would mean that he has. Currently it is all hard coded to be 'x':
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: Icon(Icons.close, color: Colors.white), // Hardcoded to be 'x'
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
// the scaffold body
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return makeLessonCard(lessons[index]);
},
),
);
I know how to do the query, I'm just not sure where to do it such that when the page loads, it is automatically updated to ticks and crosses based on the user results.
The query would be :
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) return true;
}
return false;
});
Base Auth Class I use for Authentication:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Future<String> signIn(String email, String password) async {
FirebaseUser user = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return user.uid;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user;
}
Future<void> signOut() async {
return _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
}
Update - What I tried to do:
Use ternary operator:
class _NavigationPageState extends State<NavigationPage> {
. . . // omitted code
bool passed;
#override
void initState() {
passed = false;
. . . // omitted code
super.initState();
}
checkUserPassedLesson (Lesson lesson) async {
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) {
passed = true;
return;
}
}
passed = false;
});
}
#override
Widget build(BuildContext context) {
// for lesson page
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
// query here and route accordingly
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
checkUserPassedLesson(lessons[index]);
return makeLessonCard(lessons[index]);
},
),
);
. . . // omitted code
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeLessonBody,
bottomNavigationBar: makeBottom,
);
}
}
Put Query in init:
class _NavigationPageState extends State<NavigationPage> {
... // omitted code
bool passed = false;
Container makeLessonBody;
#override
void initState() {
... // omitted code
// for lesson page
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
checkUserPassedLesson(lessons[index]);
debugPrint(passed.toString());
return makeLessonCard(lessons[index]);
},
),
);
super.initState();
}
void checkUserPassedLesson (Lesson lesson) async {
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) {
setState(() {
debugPrint(ds['pass'].toString());
passed = true;
});
}
} else {
setState(() {
passed = false;
});}
});
}
... // omitted code
#override
Widget build(BuildContext context) {
... // omitted code
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeLessonBody,
bottomNavigationBar: makeBottom,
);
}
}
Latest Update: Since the above methods were not working out, I tried to use a FutureBuilder using the ternary operator and it worked.
Full code:
class NavigationPage extends StatefulWidget {
NavigationPage({Key key, this.auth, this.userId, this.onSignedOut, this.userEmail}) : super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
final String userEmail;
#override
_NavigationPageState createState() => _NavigationPageState();
}
class _NavigationPageState extends State<NavigationPage> {
List courses;
List lessons;
String title;
TabStatus tabStatus;
bool showLessons;
bool _isLoading;
#override
void initState() {
title = COURSE_PAGE_TITLE;
_isLoading = false;
tabStatus = TabStatus.COURSE;
showLessons = false;
courses = StaticMethods.getCourses();
// temp value
lessons = StaticMethods.getLessons(Abbr.P01);
super.initState();
}
_signOut() async {
setState(() {
_isLoading = true;
});
try {
await widget.auth.signOut();
widget.onSignedOut();
setState(() {
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
print(e);
}
}
Widget _showLoading(){
if (_isLoading) {
return Center(
child: ColorLoader5(
dotOneColor: Colors.white24,
dotTwoColor: Colors.white70,
dotThreeColor: Colors.white,
dotType: DotType.circle,
dotIcon: Icon(Icons.adjust),
duration: Duration(seconds: 1),
)
);
}
return Container(height: 0.0, width: 0.0,);
}
Widget _showLoadingTile() {
return Center (
child: Container(
height: MediaQuery.of(context).size.height/10,
width: MediaQuery.of(context).size.width/2,
child: ColorLoader5(
dotOneColor: Colors.white24,
dotTwoColor: Colors.white70,
dotThreeColor: Colors.white,
dotType: DotType.circle,
dotIcon: Icon(Icons.adjust),
duration: Duration(seconds: 1),
),
)
);
}
Future<bool> checkUserPassedLesson (Lesson lesson) async {
bool passed;
await Firestore.instance
.collection('Users')
.document(widget.userEmail)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
debugPrint("querying");
if (ds.exists) {
debugPrint('exists');
passed = ds['pass'];
} else passed = false;
});
return passed;
}
#override
Widget build(BuildContext context) {
// for lesson page
ListTile makeLessonListTile(Lesson lesson, bool passed) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson, bool passed) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson, passed),
),
);
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder<bool>(
future: checkUserPassedLesson(lessons[index]),
builder: (BuildContext context,
AsyncSnapshot<bool> snapshot) {
if (snapshot.hasError) return new Text('${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: _showLoadingTile());
default:
return makeLessonCard(lessons[index], snapshot.data);
}
},
);
},
),
);
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar, // omitted code
body: makeLessonBody,
bottomNavigationBar: makeBottom, // omitted code
);
}
}
You don't have to repeat the function just to change the icon. Use a ternary operator instead (C# example but the concept is the same).
bool passed = checkUserPassedLesson(lesson);
...
IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
If passed is true it uses the done icon and close icon if its not.

How to implement a Custom dialog box in flutter?

I'm new to flutter and need to create a gallery app that needs a custom dialog box to show the selected image. How can I implement that?
Use Dialog class which is a parent class to AlertDialog class in Flutter. Dialog widget has a argument , "shape" which you can use to shape the Edges of the Dialog box.
Here is a code sample:
Dialog errorDialog = Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), //this right here
child: Container(
height: 300.0,
width: 300.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: Text('Cool', style: TextStyle(color: Colors.red),),
),
Padding(
padding: EdgeInsets.all(15.0),
child: Text('Awesome', style: TextStyle(color: Colors.red),),
),
Padding(padding: EdgeInsets.only(top: 50.0)),
TextButton(onPressed: () {
Navigator.of(context).pop();
},
child: Text('Got It!', style: TextStyle(color: Colors.purple, fontSize: 18.0),))
],
),
),
);
showDialog(context: context, builder: (BuildContext context) => errorDialog);}
Screenshot (Null Safe):
Code:
Just call this method:
void showCustomDialog(BuildContext context) {
showGeneralDialog(
context: context,
barrierLabel: "Barrier",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: Duration(milliseconds: 700),
pageBuilder: (_, __, ___) {
return Center(
child: Container(
height: 240,
child: SizedBox.expand(child: FlutterLogo()),
margin: EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(40)),
),
);
},
transitionBuilder: (_, anim, __, child) {
Tween<Offset> tween;
if (anim.status == AnimationStatus.reverse) {
tween = Tween(begin: Offset(-1, 0), end: Offset.zero);
} else {
tween = Tween(begin: Offset(1, 0), end: Offset.zero);
}
return SlideTransition(
position: tween.animate(anim),
child: FadeTransition(
opacity: anim,
child: child,
),
);
},
);
}
On a button click show dialog as -
showDialog(
context: context,
builder: (_) => LogoutOverlay(),
);
Dialog design with two buttons -
class LogoutOverlay extends StatefulWidget {
#override
State<StatefulWidget> createState() => LogoutOverlayState();
}
class LogoutOverlayState extends State<LogoutOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
margin: EdgeInsets.all(20.0),
padding: EdgeInsets.all(15.0),
height: 180.0,
decoration: ShapeDecoration(
color: Color.fromRGBO(41, 167, 77, 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, left: 20.0, right: 20.0),
child: Text(
"Are you sure, you want to logout?",
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
)),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: ButtonTheme(
height: 35.0,
minWidth: 110.0,
child: RaisedButton(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
splashColor: Colors.white.withAlpha(40),
child: Text(
'Logout',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: () {
setState(() {
Route route = MaterialPageRoute(
builder: (context) => LoginScreen());
Navigator.pushReplacement(context, route);
});
},
)),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 10.0, top: 10.0, bottom: 10.0),
child: ButtonTheme(
height: 35.0,
minWidth: 110.0,
child: RaisedButton(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
splashColor: Colors.white.withAlpha(40),
child: Text(
'Cancel',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: () {
setState(() {
/* Route route = MaterialPageRoute(
builder: (context) => LoginScreen());
Navigator.pushReplacement(context, route);
*/ });
},
))
),
],
))
],
)),
),
),
);
}
}
You just put this class in your project and call its method for showing dialog. Using this class you don't need to write dialog code everywhere
class DialogUtils {
static DialogUtils _instance = new DialogUtils.internal();
DialogUtils.internal();
factory DialogUtils() => _instance;
static void showCustomDialog(BuildContext context,
{#required String title,
String okBtnText = "Ok",
String cancelBtnText = "Cancel",
#required Function okBtnFunction}) {
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text(title),
content: /* Here add your custom widget */,
actions: <Widget>[
FlatButton(
child: Text(okBtnText),
onPressed: okBtnFunction,
),
FlatButton(
child: Text(cancelBtnText),
onPressed: () => Navigator.pop(context))
],
);
});
}
}
You can call this method like :
GestureDetector(
onTap: () =>
DialogUtils.showCustomDialog(context,
title: "Gallary",
okBtnText: "Save",
cancelBtnText: "Cancel",
okBtnFunction: () => /* call method in which you have write your logic and save process */),
child: Container(),
)
Alert Dialog
Custom Dialog
Full-Screen Dialog
ref: Flutter Alert Dialog to Custom Dialog | by Ishan Fernando | CodeChai | Medium
Alert Dialog
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Alert Dialog"),
content: Text("Dialog Content"),
actions: [
TextButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
},
);
Custom Dialog
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)), //this right here
child: Container(
height: 200,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'What do you want to remember?'),
),
SizedBox(
width: 320.0,
child: RaisedButton(
onPressed: () {},
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
color: const Color(0xFF1BC0C5),
),
)
],
),
),
),
);
});
Full-Screen Dialog
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context)
.modalBarrierDismissLabel,
barrierColor: Colors.black45,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext buildContext,
Animation animation,
Animation secondaryAnimation) {
return Center(
child: Container(
width: MediaQuery.of(context).size.width - 10,
height: MediaQuery.of(context).size.height - 80,
padding: EdgeInsets.all(20),
color: Colors.white,
child: Column(
children: [
RaisedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
color: const Color(0xFF1BC0C5),
)
],
),
),
);
});
An General E.g
showDialog(context: context,builder: (context) => _onTapImage(context)); // Call the Dialog.
_onTapImage(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
Image.network('https://via.placeholder.com/150',fit: BoxFit.contain,), // Show your Image
Align(
alignment: Alignment.topRight,
child: RaisedButton.icon(
color: Theme.of(context).accentColor,
textColor: Colors.white,
onPressed: () => Navigator.pop(context),
icon: Icon(
Icons.close,
color: Colors.white,
),
label: Text('Close')),
),
],
);
}
I usually build a wrapper for the dialog that matches the app theme and avoids much redundant code.
PlaceholderDialog
class PlaceholderDialog extends StatelessWidget {
const PlaceholderDialog({
this.icon,
this.title,
this.message,
this.actions = const [],
Key? key,
}) : super(key: key);
final Widget? icon;
final String? title;
final String? message;
final List<Widget> actions;
#override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
icon: icon,
title: title == null
? null
: Text(
title!,
textAlign: TextAlign.center,
),
titleTextStyle: AppStyle.bodyBlack,
content: message == null
? null
: Text(
message!,
textAlign: TextAlign.center,
),
contentTextStyle: AppStyle.textBlack,
actionsAlignment: MainAxisAlignment.center,
actionsOverflowButtonSpacing: 8.0,
actions: actions,
);
}
}
Usage
showDialog(
context: context,
builder: (ctx) => PlaceholderDialog(
icon: Icon(
Icons.add_circle,
color: Colors.teal,
size: 80.0,
),
title: 'Save Failed',
message: 'An error occurred when attempt to save the message',
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: Text('!Got It'),
),
],
),
);
Result
You can now use AlertDialog and in content build your widget.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))),
backgroundColor: Colors.green,
content: Container(
height:200,
width:200,
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(filepath),
fit: BoxFit.cover))),}),
There's a working solution in my case:
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: Column(
children: <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to confirm this message?'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('Confirm'),
onPressed: () {
print('Confirmed');
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Hope this is helpful:
I've made static function in a separate class:
import 'package:flutter/material.dart';
import 'package:flutter_application_1/TGColors.dart';
class TGDialog
{
static doNothing() { } // stub needed for Function parameters
/// Returns an AlertDialog with most optional parameters
static AlertDialog dlg( BuildContext context,
{ String txtTitle = 'WHAT? no title?' ,
String txtMsg = 'WHAT? no content?',
String txtBtn1 = 'CANCEL' ,
String txtBtn2 = 'OK' ,
Function funcBtn1 = doNothing ,
Function funcBtn2 = doNothing ,
Color colBackground = TGColors.Orange ,
Color colText = TGColors.Indigo } )
{
return
AlertDialog(
backgroundColor : colBackground,
title : Text(txtTitle),
content : Text(txtMsg),
actions : <Widget>
[
TextButton(
onPressed : () => { funcBtn1(), Navigator.pop(context,'Cancel')},
child : Text(txtBtn1, style: TextStyle(color: colText)),
),
TextButton(
onPressed :() => { funcBtn2(),Navigator.pop(context) },
child : Text(txtBtn2, style: TextStyle(color: colText)),
),
],
);
}
}
An example:
Positioned( bottom: 1, left: (screenW / 5.6),
child : FloatingActionButton(
heroTag : 'clear',
onPressed :() => showDialog<String>
(
context : context,
builder : (BuildContext context) =>
///////////////////////////////////////////////////////
TGDialog.dlg( context,
txtTitle : 'Clear Order?',
txtMsg : 'This resets all item counts' ,
funcBtn2 : resetOrder)
///////////////////////////////////////////////////////
),
child : const Text('clear\nall', textAlign: TextAlign.center),
shape : RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
),
),
etc.
Btw: I like CSS webcolors so I defined them in a separate class
like so:
import 'dart:ui';
/// Contains mainly web colors (based on CSS)
///
/// Usage e.g: ... = TGcolors.CornFlowerBlue
class TGColors
{
static const PrimaryColor = Color(0xFF808080);
static const AliceBlue = Color(0xFFF0F8FF);
static const AntiqueWhite = Color(0xFFFAEBD7);
static const Aqua = Color(0xFF00FFFF);
static const Aquamarine = Color(0xFF7FFFD4);
// etc.
Custom Alert Dialog in Flutter
void openAlert() {
dialog = Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)),
//this right here
child: Container(
height: 350.0,
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ClipRRect(
child: Image.asset(
"assets/images/water1.jpg",
width: double.infinity,
height: 180,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topRight: Radius.circular(16)),
),
Container(
margin: EdgeInsets.only(top: 16),
decoration: boxDecorationStylealert,
width: 200,
padding: EdgeInsets.symmetric(horizontal: 8),
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
showToastMessage("-");
},
child:Image.asset("assets/images/subtraction.png",width: 30,height: 30,)),
Text(
"1",
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: black_color),
),
GestureDetector(
onTap: () {
showToastMessage("+");
},
child:Image.asset("assets/images/add.png",width: 30,height: 30,)),
],
),
),
Expanded(child: Container()),
Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 12, right: 6),
child: MaterialButton(
onPressed: cancelClick,
color: green_color,
child: Text(
"CANCEL",
style: TextStyle(fontSize: 12, color: white_color),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4)),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 6, right: 12),
child: MaterialButton(
onPressed: okClick,
color: green_color,
child: Text(
"OK",
style: TextStyle(fontSize: 12, color: white_color),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4)),
),
),
)
],
)
],
),
),
);
showDialog(
context: context, builder: (BuildContext context) => dialog);
}