How to call seperate file dart - flutter

I want to make a condition when I press the button, it will shows pop up. But, beacuse I don't want the code to be long, I create the method on the other file. Unfortunately, the button did not respond anything.
This is where I put the method.
class AddAreaItem extends StatelessWidget {
const AddAreaItem({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: popUpDialog(context),
);
}
popUpDialog(BuildContext context) {
TextEditingController customController = TextEditingController();
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add Area'),
content: TextField(
controller: customController,
decoration: const InputDecoration(hintText: 'Area Name'),
),
actions: [
MaterialButton(
child: const Text('Add Area'),
onPressed: () {},
),
],
);
});
}
}
And this is where I call the method.
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 550, right: 55),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
const Color.fromARGB(229, 58, 0, 229),
minimumSize: const Size(50, 50)),
child: Row(
children: const [
Icon(Icons.add_box_outlined),
SizedBox(
width: 15,
),
Text('Add New Area'),
],
),
onPressed: () {
const AddAreaItem(); // <----- AddAreaItem class from seperate file
},
Any suggestion what should I do, guys?
This is the folder
This is widget tree

You can't call a Widget in onPressed, you should call the function directly
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 550, right: 55),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color.fromARGB(229, 58, 0, 229),
minimumSize: const Size(50, 50)),
child: Row(
children: const [
Icon(Icons.add_box_outlined),
SizedBox(
width: 15,
),
Text('Add New Area'),
],
),
onPressed: () {
popUpDialog(context); // <----- Function from seperate file
},
)))
and in the seperate file
popUpDialog(BuildContext context) {
TextEditingController customController = TextEditingController();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add Area'),
content: TextField(
controller: customController,
decoration: const InputDecoration(hintText: 'Area Name'),
),
actions: [
MaterialButton(
child: const Text('Add Area'),
onPressed: () {},
),
],
);
});
}

Related

Flutter 3: Row layout not showing up in a Stepper

I am working on an onboarding screen where I want to have the onboarding in 3 steps, so using the Stepper widget. The Stepper widget is inside a Column as I want to have some text displayed over the Stepper first. But now when I am trying to use a Row inside the Step widget to display some data horizontally, it does not show up. But it works if I make it a Column.
What could be causing this and any possible fix?
Flutter version: 3.3.8
What I am trying:
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 40),
const Text('Hi there!', style: AppStyles.heading),
const Text(
'Let\'s get you started',
style: AppStyles.subheading,
),
const SizedBox(
height: 50,
),
Stepper(
type: StepperType.vertical,
currentStep: _currentStep,
physics: const ScrollPhysics(),
onStepTapped: (step) => onTapped(step),
onStepContinue: onContinued,
onStepCancel: onCancel,
steps: [
Step(
title: const Text('Select a book'),
content: CustomButton(onPressed: () {}, text: 'Find Book'),
),
Step(
title: const Text('Set your goal'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const TextField(
decoration: InputDecoration(
hintText: 'Pages',
),
keyboardType: TextInputType.number,
),
const SizedBox(width: 10),
CustomButton(onPressed: () {}, text: 'Set Goal'),
],
)),
const Step(
title: Text('When you want to be reminded'),
content: TimePickerDialog(
initialTime: TimeOfDay(hour: 8, minute: 0),
))
],
controlsBuilder: (context, _) {
return Row(
children: <Widget>[
TextButton(
onPressed: () => onContinued(),
child: const Text('Next'),
),
TextButton(
onPressed: () => onCancel(),
child: const Text('Back'),
),
],
);
},
)
],
),
),
),
);
}
CustomButton widget:
class CustomButton extends StatelessWidget {
final String text;
final bool isOutlined;
final bool isLoading;
final bool isDisabled;
final VoidCallback onPressed;
const CustomButton(
{Key? key,
required this.text,
this.isOutlined = false,
this.isLoading = false,
this.isDisabled = false,
required this.onPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
backgroundColor: isOutlined ? Colors.white : Pallete.primaryBlue,
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 18),
foregroundColor: isOutlined ? Pallete.primaryBlue : null,
elevation: 4,
side: isOutlined
? const BorderSide(color: Pallete.primaryBlue, width: 1.0)
: null,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
child: Stack(
children: [
Visibility(
visible: isLoading ? false : true,
child: Text(text,
style: const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600)),
),
Visibility(
visible: isLoading,
child: Loader(
color: isOutlined ? Pallete.primaryBlue : Pallete.white,
))
],
),
);
}
}
Output
Wrap your TextField and CustomButton (while it has ElevatedButton) with Expanded widget.
Step(
title: const Text('Set your goal'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: const TextField(
decoration: InputDecoration(
hintText: 'Pages',
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 10),
Expanded(
child: CustomButton(
onPressed: () {}, text: 'Set Goal')),
],
)),
Find more about constraints

How can I fix the renderflex overflow of a card in Flutter?

How can I fix the RenderFlex overflowed pixel in my card Flutter? I cant seem to find a tutorial regarding this kind of problem. All of the tutorials in StackOverflow teach you to use the listview and SingleChildScrollView but that is not the case for me. The error shows in the card itself and I don't want the card to be using a singlechildscrollview.
I already tried fixing it by lowering the height and width but I will still need a proper tutorial that can help me fix this kind of issues.
This is the card.dart for the application
import 'package:flutter/material.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlant; //Change to String
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 1,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//image of the card
Image.asset(
imageOfPlant,
height: 200,
width: 150,
fit: BoxFit.cover,
),
SizedBox(
height: 50,
width: 150,
child: Center(
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
),
), //text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
This is the home.dart where the card will be called.
import 'package:flutter/material.dart';
import 'package:picleaf/nav_pages/plant.dart';
import '../widgets/card.dart';
class homePage extends StatefulWidget {
const homePage({super.key});
#override
State<homePage> createState() => _HomePageState();
}
List<String> plants = [
"Bell Pepper",
"Cassava",
"Grape",
"Potato",
"Strawberry",
"Tomato",
];
class CustomSearchDelegate extends SearchDelegate {
// Demo list to show querying
CustomSearchDelegate({String hinttext = "Search plants here"})
: super(searchFieldLabel: hinttext);
// first overwrite to
// clear the search text
#override
List<Widget>? buildActions(BuildContext context) {
return [
IconButton(
onPressed: () {
query = '';
},
icon: const Icon(Icons.clear),
),
];
}
// second overwrite to pop out of search menu
#override
Widget? buildLeading(BuildContext context) {
return IconButton(
onPressed: () {
close(context, null);
},
icon: const Icon(Icons.arrow_back),
);
}
// third overwrite to show query result
#override
Widget buildResults(BuildContext context) {
List<String> matchQuery = [];
for (var fruit in plants) {
if (fruit.toLowerCase().contains(query.toLowerCase())) {
matchQuery.add(fruit);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: (context, index) {
var result = matchQuery[index];
return ListTile(
title: Text(
result,
style: const TextStyle(fontFamily: 'RobotoMedium'),
),
);
},
);
}
// last overwrite to show the
// querying process at the runtime
#override
Widget buildSuggestions(BuildContext context) {
List<String> matchQuery = [];
for (var fruit in plants) {
if (fruit.toLowerCase().contains(query.toLowerCase())) {
matchQuery.add(fruit);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: (context, index) {
var result = matchQuery[index];
return ListTile(
title: Text(
result,
style: const TextStyle(fontFamily: 'RobotoMedium'),
),
);
},
);
}
}
class _HomePageState extends State<homePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
"PicLeaf",
style: TextStyle(
color: Color.fromRGBO(102, 204, 102, 1.0),
fontWeight: FontWeight.bold),
),
backgroundColor: Colors.white,
shadowColor: const Color.fromARGB(255, 95, 94, 94),
actions: [
IconButton(
onPressed: () {
// method to show the search bar
showSearch(
context: context,
// delegate to customize the search bar
delegate: CustomSearchDelegate());
},
icon: const Icon(Icons.search, color: Colors.black),
)
],
),
backgroundColor: const Color(0xffeeeeee),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 10,
),
Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: const Text(
'Take a pic!',
style: TextStyle(
fontSize: 35,
fontFamily: 'RobotoBold',
color: Colors.black),
textAlign: TextAlign.left,
),
),
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 10),
child: const Text('Find out what is wrong with your plant!',
style: TextStyle(
fontSize: 18,
fontFamily: 'RobotoMedium',
color: Color.fromRGBO(102, 124, 138, 1.0)),
textAlign: TextAlign.left),
),
const SizedBox(
height: 10,
),
Container(
color: const Color.fromRGBO(102, 204, 102, 1.0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 0),
margin: const EdgeInsets.symmetric(horizontal: 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Expanded(
child: Text('List of Plants',
style: TextStyle(
fontSize: 30,
fontFamily: 'RobotoBold',
color: Color(0xffeeeeee)),
textAlign: TextAlign.center),
),
],
),
),
GridView.count(
physics: const ScrollPhysics(),
shrinkWrap: true,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
crossAxisCount: 2,
children: <Widget>[
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
],
),
],
),
),
],
),
),
);
}
}
Your gridView's Item give you a specific size but you are setting more than that for your container and text, I suggest you try this:
child: Stack(
children: <Widget>[
//image of the card
Image.asset(
imageOfPlant,
height: double.infinity,
width: 150,
fit: BoxFit.cover,
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(
height: 50,
width: 150,
child: Center(
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
),
),
), //text //SizedBox
], // <widget>[]
),
and If you want to change item's AspectRatio you can do this:
GridView.count(
physics: const ScrollPhysics(),
shrinkWrap: true,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
crossAxisCount: 2,
childAspectRatio: 2 / 3, <--- add this
children: <Widget>[
...
]
)

Why does ontap change all cards

I am still new to coding and still learning dart. I can't find a solution anywhere. please help. when I run my code, everything works fine but when i add multiple cards, when i tap one, they all change color and have a strikethrough. I am not sure where the problem is. Please can you help, here is my code:
#override
bool _enabled = true;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('To-Do List'),
centerTitle: true,
backgroundColor: Colors.grey[900],
),
body: ListView(children: _getItems()),
backgroundColor: Colors.grey[850],
floatingActionButton: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => _displayDialog(context),
tooltip: 'Add Item',
child: Icon(Icons.add),
backgroundColor: Colors.grey[900],
),
),
);
}
void _addTodoItem(String title) {
//Wrapping it inside a set state will notify
// the app that the state has changed
setState(() {
_todoList.add(title);
});
_textFieldController.clear();
}
final Map<String, bool> _enabledMap = Map<String, bool>();
Widget _buildTodoItem(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: InkWell(
onTap: () => setState(() {
item.isTapped = !item.isTapped;
// _enabled = !_enabled;
// _color = Colors.grey;
}),
child: Container(
child: Container(
width: 50,
height: 75,
alignment: Alignment.center,
child: Text(
title,
style: TextStyle(decoration: _enabledMap[title] == true ? TextDecoration.lineThrough, color : null: Colors.grey[700]),
),
color: _color,
),
),
)
);
}
//Generate a single item widget
_displayDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add a task to your List'),
content: TextField(
controller: _textFieldController,
decoration: const InputDecoration(hintText: 'Enter task here'),
),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('ADD'),
onPressed: () {
Navigator.of(context).pop();
_addTodoItem(_textFieldController.text);
},
),
],
);
});
}
Your onTap method updates the _enabled variable in setState.
That variable is used for all the containers in their TextStyle widget.
In order to select one item, I suggest creating a map that'll store your "enabled" logic, and the key should be a unique String (the title).
Something like below: (not tested)
final Map<String, bool> _enabledMap = Map<String, bool>();
enter code here
Widget _buildTodoItem(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: InkWell(
onTap: () => setState(() { if (_enabledMap[title] == null) {_enabledMap[title] = false;} _enabledMap[title] = !_enabledMap[title]; _color = Colors.grey;}),
child: Container(
width: 50,
height: 75,
alignment: Alignment.center,
child: Text(
title,
style: TextStyle(decoration: _enabledMap[title] == true ? TextDecoration.lineThrough, color: Colors.grey[700] : null),
),
color: _color,
),
),
)
);
class Details {
String title;
bool isTapped;
Details({this.title, this.isTapped});
}
class Testing2 extends StatefulWidget {
#override
_Testing2State createState() => _Testing2State();
}
class _Testing2State extends State<Testing2> {
#override
bool _enabled = true;
List<Details> _todoList = [];
List<Widget> lists = [];
TextEditingController _textFieldController = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('To-Do List'),
centerTitle: true,
backgroundColor: Colors.grey[900],
),
body: _getItems(), //ListView(children: _getItems()),
backgroundColor: Colors.grey[850],
floatingActionButton: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => _displayDialog(context),
tooltip: 'Add Item',
child: Icon(Icons.add),
backgroundColor: Colors.grey[900],
),
),
);
}
Widget _getItems() {
return Container(
child:_todoList.length>0? ListView.builder(
itemCount: _todoList.length,
itemBuilder: (context, index) {
return _buildTodoItem(_todoList[index]);
})
:Center(child: Text("It's Empty", style: TextStyle(color: Colors.white),),),
);
}
void _addTodoItem(String title) {
setState(() {
_todoList.add(Details(isTapped: false, title: title));
});
_textFieldController.clear();
}
Widget _buildTodoItem(Details item) {
return Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: InkWell(
onTap: () => setState(() {
item.isTapped = !item.isTapped;
}),
child: Container(
width: 50,
height: 75,
alignment: Alignment.center,
child: Text(
item.title,
style: TextStyle(
decoration:
item.isTapped ? null : TextDecoration.lineThrough,
color: Colors.grey[700]),
),
color: item.isTapped ? Colors.grey : null,
// _color
),
)));
}
//Generate a single item widget
_displayDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add a task to your List'),
content: TextField(
controller: _textFieldController,
decoration: const InputDecoration(hintText: 'Enter task here'),
),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('ADD'),
onPressed: () {
Navigator.of(context).pop();
_addTodoItem(_textFieldController.text);
},
),
],
);
});
}
}

confused about setState inside onTap Flutter

I've been trying to learn Flutter and this part got me really confused. Thanks a million for your help.
I wanted to return data (an object) to the first screen from the second screen. I did successfully thanks to posts I read from the internet but haven't managed to fully understand. Specifically: Why do I need to assign categoryData to the returned object then again assign it to categoryCard? Why can't I do it directly by assigning categoryCard to the returned Object? This is code that works:
First screen:
SizedBox(
height: 95,
child: GestureDetector(
onTap: () async {
final categoryData =
await Navigator.pushNamed(context, '/EditCategory');
setState(() => categoryCard = categoryData);
},
child: categoryCard),
),
Second screen:
return Card(
child: ListTile(
title: Text('${myText[index]}'),
leading: myIcon[index],
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.pop(
context,
CategoryCard(
categoryIcon: myIcon[index],
categoryText: Text(
'${myText[index]}',
style: TextStyle(fontSize: 20),
)));
},
),
);
Code I think should work but doesn't
First screen:
SizedBox(
height: 95,
child: GestureDetector(
onTap: () {
setState(() async {
CategoryCard categoryCard =
await Navigator.pushNamed(context, '/EditCategory');
});
},
child: categoryCard),
),
Second screen is the same
This is the entire code of the first and second screen in case you'd like to know:
First:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'MyApp.dart';
class Input extends StatefulWidget {
#override
_InputState createState() => _InputState();
}
class _InputState extends State<Input> {
CategoryCard categoryCard = CategoryCard();
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(210, 234, 251, 1),
appBar: appBarInEx(),
body: TabBarView(
children: [
ListView(
children: [
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter the value';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Processing Data')));
}
},
child: Text('Submit'),
),
),
],
),
),
SizedBox(
height: 150,
child: Card(
color: Color.fromRGBO(254, 228, 194, 1),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(
height: 20,
),
Text(
'Amount',
style: TextStyle(fontSize: 30),
),
SizedBox(
height: 15,
),
Text(r'0$',
style: TextStyle(
fontSize: 30,
)),
Text('_____________________________________',
style: TextStyle(
fontSize: 15,
))
],
),
),
),
),
SizedBox(
height: 95,
child: GestureDetector(
onTap: () async {
final categoryData =
await Navigator.pushNamed(context, '/EditCategory');
setState(() => categoryCard = categoryData);
},
child: categoryCard),
),
SizedBox(
height: 95,
child: Card(
color: Color.fromRGBO(254, 228, 194, 1),
child: ListTile(
contentPadding:
EdgeInsets.symmetric(vertical: 18, horizontal: 16),
title: Text(
'Note',
style: TextStyle(fontSize: 20),
),
leading: Icon(Icons.description_outlined, size: 40),
trailing: Icon(
Icons.arrow_forward_ios_outlined,
size: 30,
),
),
),
),
SizedBox(
height: 95,
child: Card(
color: Color.fromRGBO(254, 228, 194, 1),
child: ListTile(
contentPadding:
EdgeInsets.symmetric(vertical: 18, horizontal: 16),
title: Text(
'Date',
style: TextStyle(fontSize: 20),
),
leading: Icon(Icons.event, size: 40),
trailing: Icon(
Icons.arrow_forward_ios_outlined,
size: 30,
),
),
),
),
Container(
margin: EdgeInsets.fromLTRB(30, 70, 30, 0),
child: Padding(
padding: const EdgeInsets.all(18.0),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
elevation: 10,
color: Colors.lime,
onPressed: () {
Navigator.pushNamed(context, '/');
},
textColor: Colors.white,
child: Text(
'Save',
style: TextStyle(fontSize: 40),
),
),
),
)
],
),
Container()
],
),
);
}
}
class CategoryCard extends StatelessWidget {
final Text categoryText;
final Icon categoryIcon;
CategoryCard(
{Key key,
this.categoryText = const Text(
'Category',
style: TextStyle(fontSize: 20),
),
this.categoryIcon = const Icon(
Icons.category_outlined,
size: 40,
)})
: super(key: key);
#override
Widget build(BuildContext context) {
return Card(
color: Color.fromRGBO(254, 228, 194, 1),
child: ListTile(
contentPadding: EdgeInsets.symmetric(vertical: 18, horizontal: 16),
title: categoryText,
leading: categoryIcon,
trailing: Icon(
Icons.arrow_forward_ios_outlined,
size: 30,
),
),
);
}
}
Second:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:material_floating_search_bar/material_floating_search_bar.dart';
import 'Input.dart';
import 'MyApp.dart';
class EditCategory extends StatefulWidget {
#override
_EditCategoryState createState() => _EditCategoryState();
}
class _EditCategoryState extends State<EditCategory> {
List<String> myText = [
'Add Category',
'Cosmetic',
'Education',
'Clothes',
'Food',
'Cosmetic',
'Education',
'Clothes',
'Food'
];
List<Widget> myIcon = [
Icon(Icons.add_circle_outline),
Icon(Icons.no_food_outlined),
Icon(Icons.face),
Icon(Icons.book_outlined),
Icon(Icons.g_translate),
Icon(Icons.no_food_outlined),
Icon(Icons.face),
Icon(Icons.book_outlined),
Icon(Icons.g_translate),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Color.fromRGBO(210, 234, 251, 1),
appBar: appBarInEx(),
body: TabBarView(
children: [
Column(
children: [
FloatingSearchAppBar(
title: const Text('Enter Category Name'),
transitionDuration: const Duration(milliseconds: 800),
color: Colors.orangeAccent.shade100,
colorOnScroll: Colors.greenAccent.shade200,
height: 55,
),
Expanded(
child: ListView.builder(
itemCount: myText.length,
itemBuilder: (context, index) {
if (index == 0) {
return SizedBox(
height: 90,
child: Card(
child: ListTile(
title: Text('${myText[index]}'),
leading: myIcon[index],
trailing: Icon(Icons.arrow_forward_ios),
),
));
}
return Card(
child: ListTile(
title: Text('${myText[index]}'),
leading: myIcon[index],
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.pop(
context,
CategoryCard(
categoryIcon: myIcon[index],
categoryText: Text(
'${myText[index]}',
style: TextStyle(fontSize: 20),
)));
},
),
);
},
)),
],
),
Container()
],
),
),
);
}
}
The good code waits for the onTap and assign the result into a local variable. Then it uses setState to assign the local variable into the instance field.
Why do I need to assign categoryData to the returned object then again assign it to categoryCard? Why can't I do it directly by assigning categoryCard to the returned Object?
You can do that actually, this will work:
GestureDetector(
onTap: () async {
categoryData = await Navigator.push(...);
setState(() {});
},
),
Basically setState tells Flutter to rebuild your widget, it's not strictly required to perform state setting within the callback.
Regarding bad code: you use setState with an async callback so Flutter will just rebuild your widget immediately at that moment, it doesn't wait for your async operation to complete. At the time of rebuild, categoryData has the old value so nothing changes on the screen. When user triggers onTap on the second screen and pops back to the first, categoryData is updated with the new value but your widget is not rebuilt so it still shows outdated data.

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);
}