How can I have same page destination using hero - dart

I am trying to make a flutter app. It is an app that can create containers. Then, when you press on those containers, it will open up a page. So, I tried to use hero with infinitely creatable containers. This is what I came up with:
import 'package:flutter/material.dart';
void main() => runApp(MainPage());
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.white,
body: Column(children: <Widget>[
Body(),
])));
}
}
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
final String open1 = 'open';
int count = 1;
#override
Widget build(BuildContext context) {
List cards = List.generate(count, (int i) => RCard(count));
return Expanded(
child: Container(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
reverse: true,
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: count,
itemBuilder: (context, i) {
if (i == 0) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Page(
open: open1,
)),
);
count++;
},
child: Hero(
tag: open1,
child: Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height *
0.015,
right:
MediaQuery.of(context).size.height *
0.015,
top: MediaQuery.of(context).size.width *
0.08,
bottom:
MediaQuery.of(context).size.width *
0.15),
child: Material(
borderRadius:
BorderRadius.circular(40.0),
color: Colors.white,
elevation: 8.0,
child: InkWell(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.add,
size: 30.0,
color: Colors.black,
)
]),
)))));
} else {
return cards[i];
}
}))));
}
}
class RCard extends StatefulWidget {
final int count;
RCard(this.count);
#override
RCardState createState() => RCardState();
}
class RCardState extends State<RCard> {
int count;
String open2;
#override
void initState() {
super.initState();
open2 = 'open$count';
count = widget.count;
}
#override
Widget build(BuildContext context) {
return Hero(
tag: open2,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Page(
open: open2,
)),
);
},
child: Padding(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.height * 0.015,
right: MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom: MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(40.0),
color: Colors.white,
elevation: 8.0,
child: Padding(
padding:
EdgeInsets.all(MediaQuery.of(context).size.width * 0.15),
)),
)),
);
}
}
class Page extends StatelessWidget {
final String open;
Page({this.open});
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Hero(tag: open, child: Material()),
onTap: () {
Navigator.pop(context);
},
);
}
}
But, this code only works when I create 1 container. When I create 2 containers and press on it, it gives me a black screen. How can I solve this problem?

I've figured out that all you need to do is delete the list 'cards' and change the 'cards[i]' method to 'RCard(i)'.
import 'package:flutter/material.dart';
void main() => runApp(MainPage());
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.white,
body: Column(children: <Widget>[
Body(),
])));
}
}
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
final String open1 = 'open';
int count = 1;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
reverse: true,
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: count,
itemBuilder: (context, i) {
if (i == 0) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Page(
open: open1,
)),
);
count++;
},
child: Hero(
tag: open1,
child: Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height *
0.015,
right:
MediaQuery.of(context).size.height *
0.015,
top: MediaQuery.of(context).size.width *
0.08,
bottom:
MediaQuery.of(context).size.width *
0.15),
child: Material(
borderRadius:
BorderRadius.circular(40.0),
color: Colors.white,
elevation: 8.0,
child: InkWell(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.add,
size: 30.0,
color: Colors.black,
)
]),
)))));
} else {
return RCard(i);
}
}))));
}
}
class RCard extends StatefulWidget {
final int count;
RCard(this.count);
#override
RCardState createState() => RCardState();
}
class RCardState extends State<RCard> {
int count;
String open2;
#override
void initState() {
super.initState();
open2 = 'open$count';
count = widget.count;
}
#override
Widget build(BuildContext context) {
return Hero(
tag: open2,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Page(
open: open2,
)),
);
},
child: Padding(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.height * 0.015,
right: MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom: MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(40.0),
color: Colors.white,
elevation: 8.0,
child: Padding(
padding:
EdgeInsets.all(MediaQuery.of(context).size.width * 0.15),
)),
)),
);
}
}
class Page extends StatelessWidget {
final String open;
Page({this.open});
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Hero(tag: open, child: Material()),
onTap: () {
Navigator.pop(context);
},
);
}
}

You can only have one Hero widget with the same tag value in a single class. If you want to have more than two Hero animation happen simultaneously, you have to give each Hero widgets different tag values.

Try giving a unique tag value every time you create the Hero. The black screen error is most likely because two or more Hero elements are getting the same tag value.
For example, inside the itemBuilder: (context, i) you are getting the current index in the varible i. You can use this index to make the tag value unique.
child: Hero(
tag: open1 + "$i",
child: /*Rest of your code*/
)
You can do something similar to make sure the values for all the tag elements are unique. Let me know if it helps!

Related

Flutter 'setState' of other Widget

i want to code a POS for german 'Fischbrötchen'. My problem is that the "View" of the Ordertabel dosn't update. I tried man things but nothing worked... can someone help me to point out my Problem ?
So when i click a button a Order should add to the Orders List and then update the View to display the order.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoApp(
home: MyHomePage(),
debugShowCheckedModeBanner: false,
theme: CupertinoThemeData(
brightness: Brightness.light, primaryColor: Colors.black54),
);
}
}
ValueNotifier<int> KundenId = ValueNotifier<int>(0);
List<Map<String, dynamic>> orders = [];
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
final List Getraenke = ["Fritz", "Wasser", "Bier"];
List<Map<String, dynamic>> items = [
{'name': 'Möltenorter', 'price': '4 Euro'},
{'name': 'Matjes', 'price': '4 Euro'},
{'name': 'Bismarkt', 'price': '4 Euro'},
{'name': 'Krabben', 'price': '5,50 Euro'},
{'name': 'Lachs', 'price': '5.50 Euro'},
{'name': 'Lachs Kalt', 'price': '5.50 Euro'},
];
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: RightSideContainer(),
);
}
}
class RightSideContainer extends StatefulWidget {
#override
State<StatefulWidget> createState() => RightSideContainerState();
}
class RightSideContainerState extends State<RightSideContainer> {
#override
Widget build(BuildContext context) {
return Row(
children: [
//left side, eingabe
Column(
children: [
Text("Kasse"),
Container(
height: 600,
width: MediaQuery.of(context).size.width / 2,
child: Padding(
padding: EdgeInsets.all(5.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black54,
),
alignment: AlignmentDirectional.topStart,
child: OrderTable(),
))),
],
),
//right side, Ausgabe
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(0),
color: Colors.black.withOpacity(0.5),
),
width: MediaQuery.of(context).size.width / 2,
alignment: Alignment.centerRight,
child: Column(
children: [
Container(
height: 500,
color: Colors.red,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return ButtonPrefab(items_: items[index]);
}),
),
],
))
],
);
}
}
class ButtonPrefab extends StatelessWidget {
final Map<String, dynamic> items_;
const ButtonPrefab({required this.items_});
void addOrder(name, price) {
orders.add({
'kundenId': 0,
'bestellung': name,
'price': price,
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoButton(
child: Text(items_['name']),
color: Colors.black54,
padding: EdgeInsets.all(3),
onPressed: () {
print(orders);
addOrder("name", 2.4);
KundenId.value++;
print(KundenId.value);
},
),
);
}
}
class OrderTable extends StatefulWidget {
#override
State<OrderTable> createState() => _OrderTableState();
}
class _OrderTableState extends State<OrderTable> {
#override
void initState() {
super.initState();
setState(() {});
}
void update() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text(
'Kunden ID',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
DataColumn(
label: Text(
'Bestellung',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
DataColumn(
label: Text(
'Preis',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
],
rows: orders
.map(
(order) => DataRow(
cells: [
DataCell(
Text(
order['kundenId'].toString(),
style: TextStyle(fontSize: 16),
),
),
DataCell(
Text(
order['bestellung'],
style: TextStyle(fontSize: 16),
),
),
DataCell(
Text(
order['price'].toString(),
style: TextStyle(fontSize: 16),
),
),
],
),
)
.toList(),
);
})
],
),
);
}
}
I tried to use 'set State' in my Statefull Widget but is dosn't change anything..
Deleted my previous answer and tested your code... and got it working now.
I see you have a Function named update() and you're even using it there, but should use it somewhere else as a callback Function. A callback Function helps you to edit values in your "previous" Widget that called this Widget. Read more here:
How to pass callback in Flutter
Also you have setState() in initState. Don't see the reason to have this there either. You should use setState in initState only for some kind of asyncronus reason, as explained here: Importance of Calling SetState inside initState
Call setState in "previous" Widget on button press after adding your item by using a callback Function (for short keeping, here is only the modified code):
class RightSideContainerState extends State<RightSideContainer> {
void update() { //this is a new Function
setState(() {});
}
#override
Widget build(BuildContext context) {
return Row(
children: [
//left side, eingabe
Column(
children: [
Text("Kasse"),
Container(
height: 600,
width: MediaQuery.of(context).size.width / 2,
child: Padding(
padding: EdgeInsets.all(5.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black54,
),
alignment: AlignmentDirectional.topStart,
child: OrderTable(),
))),
],
),
//right side, Ausgabe
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(0),
color: Colors.black.withOpacity(0.5),
),
width: MediaQuery.of(context).size.width / 2,
alignment: Alignment.centerRight,
child: Column(
children: [
Container(
height: 500,
color: Colors.red,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return ButtonPrefab(items_: items[index], callbackFunction: update); //give the "update (setState)" Function to the "next" Widget for calling it later
}),
),
],
))
],
);
}
}
class ButtonPrefab extends StatelessWidget {
final Map<String, dynamic> items_;
final Function callbackFunction; //get the callback Function of the calling Widget
const ButtonPrefab({required this.items_, required this.callbackFunction}); //get the callback Function of the calling Widget
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoButton(
child: Text(items_['name']),
color: Colors.black54,
padding: EdgeInsets.all(3),
onPressed: () {
print(orders);
// addOrder("name", 2.4); // you are always giving "name" and 2.4, but probably need to give the item that's being pushed
addOrder(items_['name'], items_['price']); //like this
KundenId.value++;
print(KundenId.value);
callbackFunction(); //this is the "update" Function I created in the calling Widget, but in this Widget it has a name "callbackFunction"
},
),
);
}
}
class _OrderTableState extends State<OrderTable> {
#override
void initState() {
super.initState();
// setState(() {}); // not necessary
}
// void update() { // not necessary
// setState(() {});
// }
}
There is so many problems here, that I cannot list them one by one.
The basic underlying problem here is that you think having a global variable is a good method to keep your state. It is not. Never has been. In no programming language in the last quarter of a century.
To hold your state (in your case I guess it's orders) use one of the state management patterns.
I suggest taking a look at Provider first. Not because it's the best, but because it is the easiest and explains your problem clearly:
Simple app state management
Once your applications get larger, my personal preference is BLoC, but that is a little to complex for this problem.

How do I save data from a stateful widget in Flutter?

I have implemented a TextField that allows user input in my flutter app. Now, I would like to save the user input data to a variable on pressing a button in order to use it later on. As I see it, in order to achieve this, I have to create a specific instance of the stateful widget and of its state so that when I call the widget to extract the variable, it does not create a new version of the widget with an empty TextField. The textfields in question are ProjectNameField and ProjectDescriptionField.
Here is my implementation:
ProjectDescriptionField savedDescription = ProjectDescriptionField();
ProjectNameField savedName = ProjectNameField();
AddPage savedPage = AddPage();
_AddPageState savedPageState = _AddPageState();
List<String> image_list_encoded = [];
class AddPage extends StatefulWidget {
final _AddPageState _addPageState = _AddPageState();
AddPage({Key? key}) : super(key: key);
#override
_AddPageState createState() => _AddPageState();
}
class _AddPageState extends State<AddPage> {
List<Asset> images = <Asset>[];
String _error = NOERROR;
#override
Widget build(BuildContext context) {
if (images.isEmpty)
return AppLayout(logicalHeight * 0.15);
else
return AppLayout(logicalHeight * 0.01);
}
List<Asset> getImages() {
return images;
}
Widget AppLayout(double distanceFromImages) {
return Scaffold(
appBar: AppBar(
leading: BackButton(color: Colors.white),
title: Text(CREATEPROJECT),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.check,
color: Colors.white,
),
onPressed: () {
SavedData _savedData = SavedData(
savedName._nameFieldState.myController.value.text,
savedDescription
._descriptionFieldState.myController.value.text,
savedPage._addPageState.getImages());
print(_savedData.saved_Project_Description);
print(_savedData.saved_Project_Name);
print(_savedData.saved_images.toString());
saveNewProject(_savedData);
},
),
],
),
body: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: savedName)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: savedDescription)),
),
]),
);
}
Widget getWidget() {
if (images.length > 0) {
return Container(
height: logicalHeight * 0.4,
width: logicalWidth * 0.8,
child: ImagePages());
} else {
return Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.15, 0, 0),
child: Container(child: Text(NOPICTURESSELECTED)));
}
}
PageView ImagePages() {
final PageController controller = PageController(initialPage: 0);
List<Widget> children = [];
images.forEach((element) {
children.add(Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.01, 0, logicalWidth * 0.01, 0),
child: AssetThumb(
asset: element,
width: 1000,
height: 1000,
)));
});
return PageView(
scrollDirection: Axis.horizontal,
controller: controller,
children: children,
);
}
}
class ProjectNameField extends StatefulWidget {
ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
#override
ProjectNameFieldState createState() {
return _nameFieldState;
}
}
class ProjectNameFieldState extends State<ProjectNameField> {
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTNAME,
),
maxLength: 30,
);
}
}
class ProjectDescriptionField extends StatefulWidget {
ProjectDescriptionFieldState _descriptionFieldState =
ProjectDescriptionFieldState();
#override
ProjectDescriptionFieldState createState() {
return _descriptionFieldState;
}
}
class ProjectDescriptionFieldState extends State<ProjectDescriptionField> {
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTDESCRIPTION,
),
minLines: 1,
maxLines: 5,
maxLength: 5000,
);
}
}
class SavedData {
String saved_Project_Name = "";
String saved_Project_Description = "";
List<Asset> saved_images = [];
SavedData(String saved_Project_Name, String saved_Project_Description,
List<Asset> saved_images) {
this.saved_Project_Name = saved_Project_Name;
this.saved_Project_Description = saved_Project_Description;
this.saved_images = saved_images;
}
}
I believe the problem lies here (ProjectNameField and ProjectDescriptionField are essentially the same, with only minor differences):
class ProjectNameField extends StatefulWidget {
ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
#override
ProjectNameFieldState createState() {
return _nameFieldState;
}
}
class ProjectNameFieldState extends State<ProjectNameField> {
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTNAME,
),
maxLength: 30,
);
}
}
When I let createState() return _nameFieldState instead of ProjectNameFieldState(), the following error occurs:
The createState function for ProjectDescriptionField returned an old or invalid state instance: ProjectDescriptionField, which is not null, violating the contract for createState.
'package:flutter/src/widgets/framework.dart':
Failed assertion: line 4681 pos 7: 'state._widget == null'
However, when I return ProjectNameFieldState(), then this code
onPressed: () {
SavedData _savedData = SavedData(
savedName._nameFieldState.myController.value.text,
savedDescription
._descriptionFieldState.myController.value.text,
savedPage._addPageState.getImages());
print(_savedData.saved_Project_Description);
print(_savedData.saved_Project_Name);
print(_savedData.saved_images.toString());
saveNewProject(_savedData);
}
does not save the project name.
How can I get rid of this error and save the project name and project description?
Thank you!
You can use the Form and TextFormFiled Widgets to easily save the value in your variable and also validate the form fields.
Here is the code snippet:
Declare a GlobalKey for the Form Widget
final _formKey = GlobalKey<FormState>();
In your Scaffold body where you will putting your TextFormFiled add the Form Widget as the parent for all the fields
Form(
key: _formKey,
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: TextFormField(
controller: projectNameController,
decoration: InputDecoration(
hintText: PROJECTNAME,
border: UnderlineInputBorder()),
maxLength: 30
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectName = value),
)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: TextFormField(
controller:projectDescriptionController ,
decoration: InputDecoration(
hintText: PROJECTDESCRIPTION,
border: UnderlineInputBorder()),
minLines: 1,
maxLines: 5,
maxLength: 5000,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectDescription = value))),
),
]),
),
Now in your IconButton's onPressed validate the form and use the save function to save the TextFormField values in your variable.
final form = _formKey.currentState;
if (form.validate()) {
form.save();
}
Now here in the above code form.validate() will invoke the validator and form.save() will invoke onSaved property of the TextFormField
Here is the complete code:
import 'package:flutter/material.dart';
class AddPage extends StatefulWidget {
AddPage({Key? key}) : super(key: key);
#override
_AddPageState createState() =>
_AddPageState();
}
class _AddPageState extends State<AddPage> {
final _formKey = GlobalKey<FormState>();
String savedProjectName;
String savedProjectDescription;
final projectNameController = TextEditingController();
final projectDescriptionController = TextEditingController();
double logicalHeight; //your logical height
double logicalWidth; //your logical widgth
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: BackButton(color: Colors.white),
title: Text(CREATEPROJECT),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.check,
color: Colors.white,
),
onPressed: () {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
}
},
),
],
),
body:Form(
key: _formKey,
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: TextFormField(
controller: projectNameController,
decoration: InputDecoration(
hintText: PROJECTNAME,
border: UnderlineInputBorder()),
maxLength: 30,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectName = value),
)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: TextFormField(
controller:projectDescriptionController ,
decoration: InputDecoration(
hintText: PROJECTDESCRIPTION,
border: UnderlineInputBorder()),
minLines: 1,
maxLines: 5,
maxLength: 5000,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectDescription = value),)),
),
]),
),
);
}
}

How to add a widget when a button is pressed flutter

For my flutter app I need a container that can be added when I press the add button. So I then looked at other Stack Overflow questions such as: Flutter - Render new Widgets on click and Flutter - Add new Widget on click. After, this is what I came up with.
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
var tPadding= MediaQuery.of(context).size.width * 0.08;
var bPadding= MediaQuery.of(context).size.width * 0.15;
var vPadding = MediaQuery.of(context).size.height * 0.015;
return Expanded (
child: Container (
child: NotificationListener<OverscrollIndicatorNotification> (
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: container.length,
itemBuilder: (context, i) {
return Padding (
padding: EdgeInsets.only(
left: vPadding,
right: vPadding,
top: tPadding,
bottom: bPadding
),
child: container[i]
);
},
)
)
)
);
}
}
int _count = 1;
List container = [
List.generate(_count, (int i) => ContainerCard),
AddContainer()
];
class ContainerCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Material (
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
);
}
}
class AddContainer extends StatefulWidget {
#override
AddContainerState createState() => AddContainerState();
}
class AddContainerState extends State<AddContainer> {
#override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell (
onTap: _addContainer,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon (
Icons.add,
size: 50.0,
)
]
),
)
);
}
void _addContainer() {
setState(() {
_count = _count + 1;
});
}
}
But for some reason this is not working. What is wrong and how can I fix this?
Full Code:
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp (
debugShowCheckedModeBanner: false,
home: Scaffold (
backgroundColor: Colors.white,
body: Column (
children: <Widget> [
AppBar(),
Body(),
]
)
)
);
}
}
class AppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
var abHeight = MediaQuery.of(context).size.height * 0.2;
var vPadding = MediaQuery.of(context).size.height * 0.07;
return Container (
height: abHeight,
child: Column (
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget> [
Row (
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: vPadding),
child: PoppinsText (
text: "App",
fontSize: 40.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
)
]
)
]
)
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
var tPadding= MediaQuery.of(context).size.width * 0.08;
var bPadding= MediaQuery.of(context).size.width * 0.15;
var vPadding = MediaQuery.of(context).size.height * 0.015;
return Expanded (
child: Container (
child: NotificationListener<OverscrollIndicatorNotification> (
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: container.length,
itemBuilder: (context, i) {
return Padding (
padding: EdgeInsets.only(
left: vPadding,
right: vPadding,
top: tPadding,
bottom: bPadding
),
child: container[i]
);
},
)
)
)
);
}
}
int _count = 1;
List container = [
List.generate(_count, (int i) => ContainerCard),
AddContainer()
];
class ContainerCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Material (
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
);
}
}
class AddContainer extends StatefulWidget {
#override
AddContainerState createState() => AddContainerState();
}
class AddContainerState extends State<AddContainer> {
#override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell (
onTap: _addContainer,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon (
Icons.add,
size: 50.0,
)
]
),
)
);
}
void _addContainer() {
setState(() {
_count = _count + 1;
});
}
}
class PoppinsText extends StatelessWidget {
PoppinsText ({Key key,
this.text,
this.fontSize,
this.fontWeight,
this.color}) : super(key: key);
final String text;
final double fontSize;
final FontWeight fontWeight;
final Color color;
#override
Widget build(BuildContext context) {
return Text (
text,
style: TextStyle (
fontFamily: 'Poppins',
fontWeight: fontWeight,
fontSize: fontSize,
color: color
),
);
}
}
You are using a Stateless widget. Switch to Stateful widget
Edit
Updated as per requirement.
The trick here is to use reverse property of pageview.
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
int count = 1;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
reverse: true,
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: count,
itemBuilder: (context, i) {
print(i);
if (i == 0) {
return Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height * 0.015,
right:
MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom:
MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell(
onTap: () {
setState(() {
count++;
});
},
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.add,
size: 50.0,
)
]),
)));
} else {
return Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height * 0.015,
right:
MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom:
MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
));
}
}))));
}
I don't know If you found your solution yet. This is my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
const double paddingInset = 5;
Color tileColor = Colors.grey[350];
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Test App',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
List<Widget> bodyElements = [];
int num = 0;
void addBodyElement() {
bodyElements.add(
Padding(
padding: const EdgeInsets.all(paddingInset),
child: Container(
height: 500,
width: double.infinity,
child: Center(child: Text('This is section $num')),
decoration: BoxDecoration(
color: tileColor,
borderRadius: BorderRadius.circular(10),
),
),
),
);
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Center(child: Text('Home')),
brightness: Brightness.dark,
leading: IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {
bodyElements.clear();
num = 0;
});
},
),
),
body: ListView(
children: <Widget>[
Column(
children: bodyElements,
),
],
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.add),
label: Text('Add'),
onPressed: () {
num++;
setState(() {
addBodyElement();
});
},
),
);
}
}

CheckboxListTile not working while working with real time database from firebase

I'm trying to create a List of data from online server Firebase using StreamBuilder bu the checkbox won't get checked.
I have used StreamBuilder to get the data and used LisTile widget to build the list items but the checkboxtilelist widget won't work after defining setState() function. And buildBody is defined under build Widget class.
Widget buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('hisab').snapshots(),
builder: (context, snapshots) {
if (!snapshots.hasData) {
return LinearProgressIndicator();
}
return _buildList(context, snapshots.data.documents);
}
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildListitem(context, data)).toList(),
);
}
Widget _buildListitem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot((data));
bool _values = false;
void _onChanged(bool newValue) {
setState(() {
_values = newValue;
});
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 9.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(5.0),
),
child: new ListTile(
onTap: () {
_onChanged(!_values);
},
leading: CircleAvatar(child: Text(record.name[0])),
title: new Column(
children: <Widget>[
new CheckboxListTile(
title: Text(record.name),
value: _values,
onChanged: _onChanged,
)
],
),
),
),
);
}
It's good idea if you create new stateful widget class:
class CustomListItemWidget extends StatefulWidget {
CustomListItemWidget({Key key, #required this.record}) : super(key: key);
final record;
#override
State createState() => _CustomListItemWidgetState();
}
class _CustomListItemWidgetState extends State<CustomListItemWidget> {
bool _values = false;
void _onChanged(bool newValue) {
setState(() {
_values = newValue;
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 9.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(5.0),
),
child: new ListTile(
onTap: () {
_onChanged(!_values);
},
leading: CircleAvatar(child: Text(widget.record.name[0])),
title: new Column(
children: <Widget>[
new CheckboxListTile(
title: Text(widget.record.name[0]),
value: _values,
onChanged: _onChanged,
)
],
),
),
),
);
}
}
Next, you can pass value from your method _buildListitem:
Widget _buildListitem(BuildContext context, DocumentSnapshot data) {
return CustomListItemWidget(
record: Record.fromSnapshot((data)),
);
}

Scrollable flutter popup menu

I'm trying to use flutter popup menu button, but I can't seem to make it smaller with a scroll.
Is it doable? Or am I using the wrong widget to do it?
Image below as reference, would like to show only the first 4 / 5 items, and scroll to show the rest!
Thanks in advance!
You can create your own PopUp Widget instead.
A Card wrapped into a AnimatedContainer with specific dimensions and a ListView inside.
Place this widget on your screen using Stack and Positioned widgets so it will be above other elements on the top | right.
class CustomPopup extends StatefulWidget {
CustomPopup({
#required this.show,
#required this.items,
#required this.builderFunction,
});
final bool show;
final List<dynamic> items;
final Function(BuildContext context, dynamic item) builderFunction;
#override
_CustomPopupState createState() => _CustomPopupState();
}
class _CustomPopupState extends State<CustomPopup> {
#override
Widget build(BuildContext context) {
return Offstage(
offstage: !widget.show,
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: widget.show ? MediaQuery.of(context).size.height / 3 : 0,
width: MediaQuery.of(context).size.width / 3,
child: Card(
elevation: 3,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: widget.items.length,
itemBuilder: (context, index) {
Widget item = widget.builderFunction(
context,
widget.items[index],
);
return item;
},
),
),
),
),
);
}
}
return Stack(
children: <Widget>[
Container(
color: Colors.blueAccent,
),
Positioned(
right: 0,
top: 60,
child: CustomPopup(
show: shouldShow,
items: [1, 2, 3, 4, 5, 6, 7, 8],
builderFunction: (context, item) {
return ListTile(
title: Text(item.toString()),
onTap: () {}
);
},
),
),
],
);
You can create this in two ways: the first one is PopupMenuButton widget and the second one is PopupRoute.
class HomePage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State {
Listitems = [1,2,3,4,5,6,7,8,9,10,11,12,13];
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(
child: PopupMenuButton(
child: Icon(Icons.add_shopping_cart),
offset: Offset(-1.0, -220.0),
elevation: 0,
color: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
itemBuilder: (context) {
return <PopupMenuEntry<Widget>>[
PopupMenuItem<Widget>(
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))),
child: Scrollbar(
child: ListView.builder(
padding: EdgeInsets.only(top: 20),
itemCount: items.length,
itemBuilder: (context, index) {
final trans = items[index];
return ListTile(
title: Text(
trans.toString(),
style: TextStyle(
fontSize: 16,
),
),
onTap: () {
//what would you like to do?
},
);
},
),
),
height: 250,
width: 500,
),
)
];
}),
)
You can also adjust the number of items you want to show by reducing or increasing height of the container. I also added a scrollbar just in case.
You can use maxHeight for constrains property.
...
PopupMenuButton(
constraints:
BoxConstraints(minWidth: context.maxWidth, maxHeight: 300),
...

Resources