Related
I have to create multiple files for different stalls but it seems so wrong and I know there's a better way but I just don't know how. Is there a way to create something like a page builder that will let me create multiple pages with different information from a single file. The difficult part is to make the onTap function of the images send the user to the stall_page of the selected stall. I tried doing this by making a view attribute in which I create a page and manually import the page route. But that involves creating a stall_info and stall_page for every single stall.
Instead of creating stall1_page, stall2_page and so on, can I create a generic stall function that will use the same page but just change the data? I know that's LITERALLY the point of object oriented programming languages but I'm really new to them as you'll tell my previous stupid questions.
This is the homescreen dashboard
class GridDashboard extends StatelessWidget {
Item item1 = Item(
title: 'Tray blazers',
subtitle: 'Open',
event: 'by Chef Tracy',
img: 'assets/images/tray_blazers-cr.png',
view: stallPage,
);
Item item2 = Item(
title: 'Papa Rimz',
subtitle: 'Open',
event: '',
img: 'assets/images/papa_rimz.png',
view: papaRimzPage,
);
Item item3 = Item(
title: 'W SAUCE',
subtitle: 'Open',
event: '',
img: 'assets/images/w_sauce-removebg.png',
view: wSaucePage,
);
Item item4 = Item(
title: 'African Kitchen',
subtitle: 'Open',
event: '',
img: 'assets/images/cherry-kitchen.png',
view: africanKitchenPage,
);
Item item5 = Item(
title: 'Suya Craze',
subtitle: 'Open',
event: '',
img: 'assets/images/suya_craze.png',
view: suyaCrazePage,
);
Item item6 = Item(
title: 'Zulkys cafe',
subtitle: 'Open',
event: '',
img: 'assets/images/zulkys-removeb.png',
view: zulkysCafePage,
);
Item item7 = Item(
title: 'Street food',
subtitle: 'Open',
event: '',
img: 'assets/images/street_food--removebg-.png',
view: streetFoodPage,
);
#override
Widget build(BuildContext context) {
List<Item> myList = [
item1,
item2,
item3,
item4,
item5,
item6,
item7,
];
return Flexible(
child: GridView.count(
childAspectRatio: 1.0,
padding: const EdgeInsets.only(left: 16, right: 16),
crossAxisCount: 2,
crossAxisSpacing: 18,
mainAxisSpacing: 18,
children: myList.map(
(data) {
return Container(
decoration: BoxDecoration(
color: const Color(0xff453658),
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(data.view);
},
child: Image.asset(
data.img,
width: 90, //double.infinity
),
),
const SizedBox(height: 14),
Text(
data.title,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
data.subtitle,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 10,
color: Colors.white38,
),
),
const SizedBox(height: 8),
// Text(
// data.event,
// style: const TextStyle(
// fontWeight: FontWeight.w600,
// fontSize: 11,
// color: Colors.white70,
// ),
// ),
],
),
);
},
).toList(),
),
);
}
}
class Item {
String title;
String subtitle;
String event;
String img;
String view;
Item({
required this.title,
required this.subtitle,
required this.event,
required this.img,
required this.view,
});
}
This is my stall_page:
class StallPage extends StatefulWidget {
const StallPage({super.key});
#override
State<StallPage> createState() => _StallPageState();
}
class _StallPageState extends State<StallPage> {
var selected = 0;
final pageController = PageController();
final stall = Stall.generateRestaurant1();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff392850), //kBackground,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomAppBar(
Icons.arrow_back_ios_outlined,
Icons.search_outlined,
leftCallback: () => Navigator.of(context).pop(),
),
StallInfo(), //
FoodList(
selected,
(int index) {
setState(() {
selected = index;
});
pageController.jumpToPage(index);
},
stall,
),
Expanded(
child: FoodListView(
selected,
(int index) {
setState(() {
selected = index;
});
},
pageController,
stall,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
height: 60,
child: SmoothPageIndicator(
controller: pageController,
count: stall.menu.length,
effect: CustomizableEffect(
dotDecoration: DotDecoration(
width: 8,
height: 8,
color: Colors.grey.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
activeDotDecoration: DotDecoration(
width: 10,
height: 10,
color: kBackground,
borderRadius: BorderRadius.circular(10),
dotBorder: const DotBorder(
color: kPrimaryColor,
padding: 2,
width: 2,
),
),
),
onDotClicked: (index) => pageController.jumpToPage(index),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: kPrimaryColor,
elevation: 2,
child: const Icon(
Icons.shopping_cart_outlined,
color: Colors.black,
size: 30,
),
),
);
}
}
This is my stall_info
class StallInfo extends StatelessWidget {
final stall = Stall.generateRestaurant1();
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 40),
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
stall.name,
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Row(
children: [
Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.blueGrey.withOpacity(0.4),
borderRadius: BorderRadius.circular(5),
),
child: Text(
stall.label,
style: const TextStyle(
color: Colors.white,
),
)),
const SizedBox(
width: 10,
),
],
)
],
),
ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.asset(
stall.logoUrl,
width: 80,
),
),
],
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
stall.desc,
style: const TextStyle(fontSize: 16),
),
Row(
children: [
const Icon(
Icons.star_outline,
color: Colors.amber,
),
Text(
'${stall.score}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 15),
],
)
],
)
],
),
);
}
}
And this is stall
class Stall {
String name;
String label;
String logoUrl;
String desc;
num score;
Map<String, List<Food>> menu;
Stall(
this.name,
this.label,
this.logoUrl,
this.desc,
this.score,
this.menu,
);
static Stall generateRestaurant1() {
return Stall(
'Tray blazers',
'Restaurant',
'assets/images/tray_blazers.jpg',
'Tray Blazers by Chef Tracy',
4.5,
{
'Recommended': Food.generateRecommendedFoods1(),
'Popular': Food.generatePopularFoods1(),
'Smoothie': [],
'Rice': [],
},
);
}
}
If I understand the question correctly, you want to open the StallPage but show different values on the page depending on which image (pertaining to a given 'Stall') was selected on the previous page? I.e. clicking on item2 should open the StallPage with the restaurant title "Papa Rimz" etc.?
In that case, you can pass the argument to your new route builder via the onTap() function as a constructor parameter instead of calling Stall.generateRestaurant1() with hardcoded values in a given dart file.
StallInfo
Instead of getting your stall data inside the build method, you simply accept it as a required parameter for your widget. Now you have access to the data (title, ...) anywhere inside here.
class StallInfo extends StatelessWidget {
// Contains the stall object with its name, label, menu etc.
final Stall stall;
StallInfo({super.key, required this.stall});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 40),
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
...
),
);
}
}
HomeScreen
I'm a bit confused as to what the item list in your your home screen is for. Are these food items in a restaurant? Because if so, I think it would be much easier to save them inside the stall as a list of items and then use that list here:
List<Stall> _stalls = [...];
I'd like to note here that you hardcoded all the items by name and then, in your build method, added them to a list. Since you don't need their names anywhere, it would be just a little bit better to move the List<Stall> myList outside the build method and simply assign the objects directly (that is, before you add a real database):
class GridDashboard extends StatelessWidget {
List<Stall> _stalls = [
Stall('Tray blazers', ...),
Stall('Papa Rimz', ...),
];
#override
Widget build(BuildContext context) {
// do something with your stalls, onTap, pass the element directly
....
children: _stalls.map(
(data) {
return GestureDetector(
onTap: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => StallPage(stall: data)
));
}
);
}),
}
}
If you use a builder function for your GridView (which you should if there can be a lot of stalls), in the onTap() you can instead call:
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => StallPage(stall: _stalls.elementAt(index))
));
StallPage
This page will look something like this
class StallPage extends StatefulWidget {
final Stall stall; // Take in the stall you passed from your home screen
const StallPage({super.key, required this.stall});
#override
State<StallPage> createState() => _StallPageState();
}
class _StallPageState extends State<StallPage> {
var selected = 0;
final pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
...
StallInfo(stall: widget.stall), // This is how you can access the values passed inside a StatefulWidget
...
);
}
}
I wrote the code to get data from List to chips and when click chips the colour changed to blue. But I want to fetch data from firestore instead "words list".
Instead this words list ...
Database collection image
I want to display "WordName" field in the chips.
My code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
class _uitryState extends State<uitry> {
List<String> wordList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String> selectedWord = [];
List<String>? deSelectedWord = [];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Config.app_background4), fit: BoxFit.fill),
),
child: SafeArea(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 14, right: 0),
child: Column(
children: [
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: wordList.map(
(word) {
bool isSelected = false;
if (selectedWord!.contains(word)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedWord!.contains(word)) {
if (selectedWord!.length < 50) {
selectedWord!.add(word);
deSelectedWord!.removeWhere(
(element) => element == word);
setState(() {});
print(selectedWord);
}
} else {
selectedWord!.removeWhere(
(element) => element == word);
deSelectedWord!.add(word);
setState(() {
// selectedHobby.remove(hobby);
});
print(selectedWord);
print(deSelectedWord);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
word,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
],
),
),
],
))),
),
);
}
}
How get that from firestore? I hope You can understand what I ask. Thank you!
I would do the following:
Initialize your list of words to an empty list
Use the initState method of the stateful widget to make a call to firestore to fetch all the documents that have the wordName property and get the word from the result and set it to a new list
Assign the new list to the wordList property and setState to re-render.
This would be it to get the words and set the chips with fetched words.
Keep in mind that since you are making an async call to firestore you should show some form of loading to tell the user you are fetching the data otherwise you would show and empty chip list until you fetch the data.
I am trying to implement this design where one chip is clickable at a time
Design Image
I tried multiple approaches like listview and grid view builders, but nothing gave me precisely what I was looking for. I eventually settled for the Wrap() widget with a list used to map Animated Container widgets, it gave me the look I wanted, but when I clicked on one chip, everything changes color instead of one like this.
How I can implement those grids of chips dynamically with the property of only one chip changing color at a time and from there be able to navigate to the next page. For more context, this is the code I used,
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:scree/constants.dart';
class SiteAbooutScreen extends StatefulWidget {
const SiteAbooutScreen({Key? key}) : super(key: key);
#override
_SiteAbooutScreenState createState() => _SiteAbooutScreenState();
}
class _SiteAbooutScreenState extends State<SiteAbooutScreen> {
bool _isTapped = false;
#override Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 74.75),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 16,
runSpacing: 16,
children: _stuff
.map(
(strings) => GestureDetector(
onTap: () {
setState(() {
_isTapped = !_isTapped;
});
},
child: AnimatedContainer(
padding:
EdgeInsets.symmetric(horizontal: 24, vertical: 10),
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: _isTapped ? primary1 : Colors.transparent,
border: Border.all(color: Colors.black54),
borderRadius: BorderRadius.circular(100)),
child: Text(
strings,
style: Small.copyWith(
fontSize: 14,
color:
_isTapped ? Colors.white : Color(0xff929292)),
),
),
),
)
.toList(),
)
],
),
),
);
}
List<String> _stuff = [
'Portfolio',
'Art',
'Marketing',
'Education',
'Blog',
'Travel',
'Fashion',
'Beauty',
'Design',
'Online Store',
'Fitness',
'Food'
];
}
This is happening because you are using single bool to handle every item. That's why everything change at the same time. In order to handle one at a time, you need to check separately.
For this, you can create list of bool or String for selected items.
class SiteAbooutScreen extends StatefulWidget {
const SiteAbooutScreen({Key? key}) : super(key: key);
#override
_SiteAbooutScreenState createState() => _SiteAbooutScreenState();
}
class _SiteAbooutScreenState extends State<SiteAbooutScreen> {
List<String> tappedItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 74.75),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 16,
runSpacing: 16,
children: _stuff
.map(
(strings) => GestureDetector(
onTap: () {
setState(() {
if (tappedItems.contains(strings)) {
tappedItems.remove(strings);
} else {
tappedItems.add(strings);
}
});
},
child: AnimatedContainer(
padding:
EdgeInsets.symmetric(horizontal: 24, vertical: 10),
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: tappedItems.contains(strings)
? Theme.of(context).primaryColor
: Colors.transparent,
border: Border.all(color: Colors.black54),
borderRadius: BorderRadius.circular(100)),
child: Text(
strings,
style: TextStyle(
fontSize: 14,
color: tappedItems.contains(strings)
? Colors.white
: Color(0xff929292)),
),
),
),
)
.toList(),
)
],
),
),
);
}
List<String> _stuff = [
'Portfolio',
'Art',
'Marketing',
'Education',
'Blog',
'Travel',
'Fashion',
'Beauty',
'Design',
'Online Store',
'Fitness',
'Food'
];
}
So I'm trying to make a drop down menu for each options and insert a different List of items each. First of all, because my Dropdownmenu widget shares the same properties for one and another, I extracted the widget to another class name "MenuDropDown". Here is the code for the widget.
import 'package:flutter/material.dart';
import 'List.dart';
class MenuDropDown extends StatefulWidget {
final String dropdownText;
final List<DropdownMenuItem<String>> itemList;
MenuDropDown({this.dropdownText, this.itemList});
#override
_MenuDropDownState createState() => _MenuDropDownState();
}
class _MenuDropDownState extends State<MenuDropDown> {
String selectedItem;
List<DropdownMenuItem> getGroomingTypeList() {
List<DropdownMenuItem<String>> dropdownItems = [];
for (String groomingType in groomingTypeList) {
var newItem = DropdownMenuItem(
child: Text(groomingType),
value: groomingType,
);
dropdownItems.add(newItem);
}
return dropdownItems;
}
List<DropdownMenuItem> getCatBreedsList() {
List<DropdownMenuItem<String>> dropdownItems = [];
for (String catBreed in catBreedsList) {
var newItem = DropdownMenuItem(
child: Text(catBreed),
value: catBreed,
);
dropdownItems.add(newItem);
}
return dropdownItems;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 10.0),
child: Container(
width: 325.0,
height: 50.0,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black45,
offset: Offset(2.5, 5.5),
blurRadius: 5.0,
)
],
borderRadius: BorderRadius.circular(8),
color: Colors.white,
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: selectedItem,
hint: Padding(
padding: const EdgeInsets.fromLTRB(22.0, 0, 0, 0),
child: Text(
widget.dropdownText,
style: TextStyle(),
),
),
items: widget.itemList,
onChanged: (value) {
setState(() {
selectedItem = value;
});
}),
),
),
);
}
}
above here I created a method to get the list item from the list class that I already created, First it works if I hardcoded the method into the Dropdownmenu's items properties to show the item list, but because I need to use the different list for the different widget, so I think if I try to create a variable of List named itemList, so I can access it from the other class where I can call just the customized variable.
And this is the Stateful widget where i use my Extracted Dropdownmenu widget :
import 'package:flutter/material.dart';
import 'TitleName.dart';
import 'dropdownmenu.dart';
class InformationDetail extends StatefulWidget {
#override
_InformationDetailState createState() => _InformationDetailState();
}
class _InformationDetailState extends State<InformationDetail> {
MenuDropDown _menuDropDown = MenuDropDown();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(25.0, 68.0, 70.0, 26.0),
child: Text(
'Information Detail',
style: TextStyle(fontSize: 35.0),
),
),
Column(
// Wrap Column
children: <Widget>[
Column(
children: <Widget>[
TitleName(
titleText: 'Grooming Type',
infoIcon: Icons.info,
),
MenuDropDown(
dropdownText: 'Grooming Type...',
//I'm trying to implement the list here with a custom variable that contain
a method with different list in dropdownmenu class
itemlist: getGroomingTypeList(),
),
TitleName(
titleText: 'Cat Breeds',
),
MenuDropDown(
dropdownText: 'Cat Breeds...',
),
TitleName(
titleText: 'Cat Size',
infoIcon: Icons.info,
),
MenuDropDown(
dropdownText: 'Cat Size...',
),
TitleName(
titleText: 'Add-On Services',
),
MenuDropDown(
dropdownText: 'Add - On Services...',
),
Container(
margin: EdgeInsets.fromLTRB(0, 15.0, 0, 0),
width: 75.0,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.black,
),
borderRadius: BorderRadius.circular(12.0),
),
child: IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context)
.pushNamed('/ReservationDetail');
},
),
),
],
),
],
),
],
),
),
),
);
}
}
And this is the list that I want to use for each of dropdown menu widget
const List groomingTypeList = ['Basic Grooming', 'Full Grooming'];
const List catBreedsList = [
'Persia',
'Anggora',
'Domestic',
'Maine Coon',
'Russian Blue',
'Slamese',
'Munchkin',
'Ragdoll',
'Scottish Fold',
];
const List catSizeList = [
'Small Size',
'Medium Size',
'Large Size',
'Extra Large Size',
];
const List addOnServicesList = [
'Spa & Massage',
'Shaving Hair / Styling',
'Injection Vitamis Skin & Coat',
'Cleaning Pet House and Environment',
'Fur Tangled Treatment',
];
I got stuck from there. How to use a different list for each dropdown menu widget that I made, because I already tried to make a constructor, variable, and method so I can use it separately, but instead I got an error that says,
type 'List' is not a subtype of type 'List<DropdownMenuItem>'
I think I implemented it in the wrong way in the first place. I really need help with another solution to these problems. thank you, everyone!
Just check out the code that I have made some changes :
import 'package:flutter/material.dart';
void main() => runApp(InformationDetail());
class InformationDetail extends StatefulWidget {
#override
_InformationDetailState createState() => _InformationDetailState();
}
class _InformationDetailState extends State<InformationDetail> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Container(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(25.0, 68.0, 70.0, 26.0),
child: Text(
'Information Detail',
style: TextStyle(fontSize: 35.0),
),
),
Column(
// Wrap Column
children: <Widget>[
Column(
children: <Widget>[
Text(
'Grooming Type',
),
MenuDropDown(
dropdownText: 'Grooming Type...',
//I'm trying to implement the list here with a custom variable that contain
// a method with different list in dropdownmenu class
type: "groomingType",
),
Text(
'Cat Breeds',
),
MenuDropDown(
dropdownText: 'Cat Breeds...',
type: "catBreeds",
),
Text(
'Cat Size',
),
MenuDropDown(
dropdownText: 'Cat Size...',
type: "catSize",
),
Text(
'Add-On Services',
),
MenuDropDown(
dropdownText: 'Add - On Services...',
type: "addOnServices",
),
Container(
margin: EdgeInsets.fromLTRB(0, 15.0, 0, 0),
width: 75.0,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.black,
),
borderRadius: BorderRadius.circular(12.0),
),
child: IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context)
.pushNamed('/ReservationDetail');
},
),
),
],
),
],
),
],
),
),
),
),
);
}
}
class MenuDropDown extends StatefulWidget {
final String dropdownText;
final String type;
MenuDropDown({this.dropdownText, this.type});
#override
_MenuDropDownState createState() => _MenuDropDownState();
}
class _MenuDropDownState extends State<MenuDropDown> {
String selectedItem;
List<String> dropdownItems = [];
List<String> groomingTypeList = ['Basic Grooming', 'Full Grooming'];
List<String> catBreedsList = [
'Persia',
'Anggora',
'Domestic',
'Maine Coon',
'Russian Blue',
'Slamese',
'Munchkin',
'Ragdoll',
'Scottish Fold',
];
List<String> catSizeList = [
'Small Size',
'Medium Size',
'Large Size',
'Extra Large Size',
];
List<String> addOnServicesList = [
'Spa & Massage',
'Shaving Hair / Styling',
'Injection Vitamis Skin & Coat',
'Cleaning Pet House and Environment',
'Fur Tangled Treatment',
];
List<String> getListBasedOnName(String value) {
print(value);
switch (value) {
case "groomingType":
return groomingTypeList;
break;
case "catBreeds":
return catBreedsList;
break;
case "catSize":
return catSizeList;
break;
case "addOnServices":
return addOnServicesList;
break;
}
return null;
}
#override
void initState() {
super.initState();
print(widget.type);
dropdownItems = getListBasedOnName(widget.type);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 10.0),
child: Container(
width: 325.0,
height: 50.0,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black45,
offset: Offset(2.5, 5.5),
blurRadius: 5.0,
)
],
borderRadius: BorderRadius.circular(8),
color: Colors.white,
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: selectedItem,
hint: Padding(
padding: const EdgeInsets.fromLTRB(22.0, 0, 0, 0),
child: Text(
widget.dropdownText,
style: TextStyle(),
),
),
items: dropdownItems.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedItem = value;
});
}),
),
),
);
}
}
As some of the widgets were missing, I have added mine you can change as per your needs. Let me know if it works.
I have a model class which I used to create a object from one of my main stateful class. I have a text field and button in my main class. But they both are completely different stateful class. That is, I have 3 different classes in a dart file (main page, textfield, button). I want to access and modify the object initiated in the build of main page in my textfield and button.
What I did: I have made my object on the top of all classes in my dart file assuming all classes have access to them. That was a success. All classes have access to that object, even value initiated to the object from main page are available in other classes (textfield, button).
Problem what I have now: Even though I have access to those values in the object, I can't modify it to latest value from FancyTextField class regardless StatusButton class updating.
What my project do: I will get some values from firestore database on my main page build and I will pass it to textfield and button in the other two classes., that way it resembles my values on it. And I will save the modified value from textfield and button to the object and upload it to firestore database from main page.
Complete code of the page:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:om/models/machine.dart';
import 'package:om/utils/kalaicons_icons.dart';
Machine machine;
class WorkshopTool extends StatefulWidget {
final String rotary;
WorkshopTool(this.rotary);
#override
_WorkshopToolState createState() => _WorkshopToolState(rotary);
}
class _WorkshopToolState extends State<WorkshopTool> {
String rotary;
bool _showOnScreen = true;
_WorkshopToolState(this.rotary);
#override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
return FutureBuilder(
future: Firestore.instance
.collection('productionrpt')
.document(rotary)
.get(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Container(
height: screenSize.height - 50.0,
child: Center(
child: SizedBox(
height: 80.0,
width: 80.0,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.green[600]),
strokeWidth: 15.0,
)),
));
machine = Machine.fromMapObjext(snapshot.data);
return Container(
height: screenSize.height - 50.0,
width: screenSize.width,
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
//To make a empty column space for stack on top
Container(
height: 80.0,
padding: EdgeInsets.only(bottom: 5.0, right: 15.0),
child: Align(
alignment: Alignment.bottomRight,
child: Text(
machine.date,
style: TextStyle(
color: Colors.black,
fontSize: 17.0,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold),
),
),
),
FancyTextField('Production'),
FancyTextField('Damages'),
FancyTextField('Plan'),
SizedBox(
height: 20.0,
),
Padding(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(context).viewInsets.bottom)),
],
),
),
Container(
height: 50.0,
decoration: BoxDecoration(
color: Colors.green[400],
borderRadius:
BorderRadius.vertical(top: Radius.circular(15.0))),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: MaterialButton(
color: Colors.black,
padding: EdgeInsets.only(
top: 10.0, bottom: 10.0, left: 20.0, right: 20.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
child: Text(
'UPDATE',
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
onPressed: () {
print('Saved to cloud : ${machine.production}');
firebasePutData();
}),
),
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.all(20.0),
child: StatusButton(),
),
),
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: Icon(
Icons.close,
size: 40,
color: Colors.black,
),
onPressed: () {
print('Bottomsheet closed');
}),
),
Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
machine.rotary,
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
decoration: TextDecoration.underline,
fontWeight: FontWeight.bold),
),
),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Align(
alignment: Alignment.topRight,
child: Switch(
value: _showOnScreen,
activeTrackColor: Colors.black54,
activeColor: Colors.black,
inactiveThumbColor: Colors.grey[600],
inactiveTrackColor: Colors.grey[500],
onChanged: (v) {
_showOnScreen = !_showOnScreen;
print('Switch tapped');
}),
),
),
],
),
);
});
}
void firebasePutData() {
Firestore.instance
.collection("productionrpt")
.document(rotary)
.updateData(machine.toMap());
print('Data updated');
}
} //End of main page STATE (a bottom sheet)
//#######################################################################################################
//############ FANCY TEXT FIELD FOR ENTERING MACHINE DATA #################
class FancyTextField extends StatefulWidget {
final String _title;
FancyTextField(
this._title,
);
#override
_FancyTextFieldState createState() => _FancyTextFieldState(this._title);
}
class _FancyTextFieldState extends State<FancyTextField> {
final String _title;
final TextEditingController _ctrl = TextEditingController();
_FancyTextFieldState(this._title);
#override
void initState() {
switch (_title) {
case 'Production':
_ctrl.text = machine.production.toString();
break;
case 'Plan':
_ctrl.text = machine.plan.toString();
break;
case 'Damages':
_ctrl.text = machine.damage.toString();
break;
}
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
height: 125.0,
margin: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.green[400],
borderRadius: BorderRadius.circular(15.0),
// boxShadow: [
// BoxShadow(
// blurRadius: 5, color: Colors.green[300], spreadRadius: 5)
// ]
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_title,
style: TextStyle(
color: Colors.black,
fontSize: 23.0,
fontWeight: FontWeight.bold),
),
Container(
height: 50,
width: 300,
alignment: Alignment.center,
padding: const EdgeInsets.all(5.0),
margin: const EdgeInsets.only(
top: 10.0, bottom: 10, left: 30.0, right: 30.0),
decoration: BoxDecoration(
color: Colors.white70,
borderRadius: BorderRadius.circular(10),
),
child: TextField(
//maxLength: 5,
controller: _ctrl,
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
style: TextStyle(
color: Colors.black,
fontSize: 30.0,
),
decoration: InputDecoration(
border: InputBorder.none,
),
onChanged: (v) {
switch (_title) {
case 'Production':
machine.production = int.parse(_ctrl.text);
break;
case 'Plan':
machine.plan = int.parse(_ctrl.text);
break;
case 'Damages':
machine.damage = int.parse(_ctrl.text);
break;
}
print('Prod: ${machine.production}');
},
),
),
],
),
);
}
} //END OF CLASS FANCY TEXT FIELD
//######################################################################################################
//####### A STATEFUL WIDGET FOR MACHINE STATUS BUTTON : running, off, breakdown ##########
class StatusButton extends StatefulWidget {
#override
_StatusButtonState createState() => _StatusButtonState();
}
class _StatusButtonState extends State<StatusButton> {
Color color;
IconData icon;
#override
Widget build(BuildContext context) {
switch (machine.stats) {
case 0:
color = Colors.grey[600];
icon = Icons.power_settings_new;
break;
case 1:
color = Colors.blue;
icon = Icons.power_settings_new;
break;
default:
color = Colors.red;
icon = Kalaicons.breakdown;
break;
}
return Container(
height: 70.0,
width: 70.0,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
child: IconButton(
icon: Icon(
icon,
color: Colors.white,
size: 50.0,
),
onPressed: () {
setState(() {
machine.stats > 1 ? machine.stats = 0 : machine.stats++;
print('Status button pressed: ${machine.stats}');
});
},
));
}
} //END OF CLASS STATUS BUTTON
My model:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
class Machine {
int _production;
int _plan;
int _damage;
int _stats = 0;
String _date = '~Not available';
String _rotary;
// Machine(this._production, this._damage, this._date,
// [this._stats, this._plan]);
int get production => this._production;
int get plan => this._plan;
int get damage => this._damage;
int get stats => this._stats;
String get date => this._date;
String get rotary => this._rotary;
set production(int updatedValue) {
if (updatedValue != null) {
this._production = updatedValue;
}
}
set plan(int updatedValue) {
if (updatedValue != null) {
this._plan = updatedValue;
}
}
set damage(int updatedValue) {
if (updatedValue != null) {
this._damage = updatedValue;
}
}
set stats(int updatedValue) {
this._stats = updatedValue;
}
// set date(String updatedValue) {
// this._date = DateFormat.jm().format(DateTime.now());
//
// }
//Function to set a map list of new data for firebase
Map<String, dynamic> toMap() {
var map = Map<String, dynamic>();
map['production'] = this._production;
map['plan'] = this._plan;
map['damages'] = this._damage;
map['stats'] = this._stats;
map['date'] = DateFormat("MMMM dd, hh:mm a").format(DateTime.now());
return map;
}
//Constructor to extract firebase collections
Machine.fromMapObjext(DocumentSnapshot map) {
this._production = map['production'];
this._plan = map['plan'];
this._damage = map['damages'];
this._stats = map['stats'];
this._date = map['date'];
this._rotary = map['machine'];
}
}
UPDATE I RECENTLY NOTED: The value of StatusButton is gettiong updated to the object and to firestore. however value of updated FancyTextField only reflects inside that class itself. Not updating globally.
Lastly I found the issue myself rolling 3 days with it. Issue was happened because when keyboard pops, the widget also gets it state rebuild. Since I had my firebase data on FutureBuilder in the build, my old data was recalled from the firebase again and save it on top of newly edited data.
Why initial text I put on the TextField didn't changed ?
Because I had it set it on my initState of FancyTextField class, so when rebuilding widget I won't execute and my edited value stays as before.
Why StatusButton value updated without failing ?
Because when I tap on the button keyboard doesn't pops up and build wasn't rebuild again. But later I noticed, after changing my StatusButton status to someother value and tapping on TextField makes it to OLD value (that is the value that is in firebase currently). Since build is recreated. That's how I figured it out.
What I did to overcome this:
I simply removed my FutureBuilder which gets data from firebase, and created a Future for the same and initiated in the initState.
If anyone want, I can show the updated code here