If I change the value of the height by moving the slider, the values of the weight and age change back to their initial values. it should not be so, what am I doing wrong?
Initially, I thought the codes to the slider value and the weight and age values were connected, but I do not see any connection between them except for the style they use together; kLabelTextStyle and kNumberTextStyle
WeightAndAge.dart
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'constants.dart';
class ColumnWidgetForWeightAndAge extends StatefulWidget {
int number;
final String info;
ColumnWidgetForWeightAndAge({
this.number,
this.info,
});
#override
_ColumnWidgetForWeightAndAgeState createState() =>
_ColumnWidgetForWeightAndAgeState();
}
class _ColumnWidgetForWeightAndAgeState
extends State<ColumnWidgetForWeightAndAge> {
#override
Widget build(BuildContext context) {
Widget minus = FloatingActionButton(
onPressed: () {
setState(() {
if (widget.number > 0) {
widget.number = widget.number - 1;
}
});
print(widget.number);
},
backgroundColor: Color(0xFF4C4F5E),
child: Icon(
FontAwesomeIcons.minus,
color: Colors.white,
),
);
Widget plus = FloatingActionButton(
backgroundColor: Color(0xFF4C4F5E),
onPressed: () {
setState(() {
if (widget.number > 0) {
widget.number = widget.number + 1;
}
});
print(widget.number);
},
child: Icon(
FontAwesomeIcons.plus,
color: Colors.white,
),
);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.info,
style: kLabelTextStyle,
),
Text(
widget.number.toString(),
style: kNumberTextStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
minus,
SizedBox(
width: 10,
),
plus,
],
)
],
);
}
}
input_page.dart
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'icons.dart';
import 'containers.dart';
import 'constants.dart';
import 'weightAndAge.dart';
enum Gender { male, female }
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
Gender selectedCardColor;
int height = 192;
int weight = 6;
int age = 18;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('BMI CALCULATOR')),
),
body: Column(
children: [
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(
gestureDetector: () {
setState(() {
selectedCardColor = Gender.male;
});
},
cardChild: ColumnData1(
info: 'MALE',
icon: FontAwesomeIcons.mars,
),
colour: selectedCardColor == Gender.male
? kActiveCardColor
: kInactiveCardColor,
),
),
Expanded(
child: ReusableCard(
gestureDetector: () {
setState(() {
selectedCardColor = Gender.female;
});
},
cardChild: ColumnData1(
info: 'FEMALE',
icon: FontAwesomeIcons.venus,
),
colour: selectedCardColor == Gender.female
? kActiveCardColor
: kInactiveCardColor,
),
),
],
),
),
Expanded(
child: ReusableCard(
colour: kActiveCardColor,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'HEIGHT',
style: kLabelTextStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(height.toString(), style: kNumberTextStyle),
Text('cm', style: kLabelTextStyle),
],
),
SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.white,
thumbColor: Color(0xFFDD0059),
overlayColor: Color(0x29DD0059),
thumbShape: RoundSliderThumbShape(
enabledThumbRadius: 18,
),
overlayShape:
RoundSliderOverlayShape(overlayRadius: 30)),
child: Slider(
min: 120,
max: 220,
value: height.toDouble(),
inactiveColor: Color(0xFF8D8E98),
onChanged: (double newValue) {
setState(() {
height = newValue.round();
});
},
),
)
],
),
),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(
colour: kActiveCardColor,
cardChild: ColumnWidgetForWeightAndAge(
info: 'WEIGHT',
number: weight,
),
),
),
Expanded(
child: ReusableCard(
colour: kActiveCardColor,
cardChild: ColumnWidgetForWeightAndAge(
info: 'AGE',
number: age,
),
),
),
],
),
),
Container(
margin: EdgeInsets.only(top: kContainerMarginTop),
color: Color(0xFFDD0059),
height: kBottomContainerHeight,
width: double.infinity,
)
],
),
);
}
}
When you change the Height slider, it calls setState, which rebuilds the whole Widget InputPage. Since ColumnWidgetForWeightAndAge is a child of that widget, it also rebuilds with the values you pass to it, which are still 6 and 18 in the InputPage, which is why it gets reinitialized.
There are a few issues with your code:
You should refactor so that setState only rebuilds parts of the UI that actually needs rebuild. For instance, the Slider should be be refactored in a separated Widget. That will already make things a bit clearer and is good practice.
In ColumnWidgetForWeightAndAge the int number; is not final, which should generate a warning. Make it final, this will be the initial value that the Widget receives. Then in the _ColumnWidgetForWeightAndAgeState, create an initState method to initialize a local variable myNumber that will receive the widget.number as initial value.
If you want to update a variable in a parent widget, you have a to pass a callback to its child (such as void onChange(int newValue)=>weight=newValue;), that you will call from the child widget each time the value updates. So your onPressed will become:
onPressed: () {
setState(() {
if (myNumber > 0) {
myNumber++;
}
});
print(myNumber);
widget.callback(myNumber);
}
Related
Im trying to make something in flutter that looks like a twitter clone. The Tweet, called a wave, can be seen in the following:
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timeago/timeago.dart' as timeago;
import '../../../../../../blocs/vote/vote_bloc.dart' as vote;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:like_button/like_button.dart';
import '../../../../../../blocs/profile/profile_bloc.dart';
import '../../../../../../blocs/wave_liking/wave_liking_bloc.dart';
import '../../../../../../models/user_model.dart';
import '../../../../../../models/wave_model.dart';
import '../../../../../../widgets/text_splitter.dart';
import '../../generic_view.dart';
import '../../photo_view/photo_view.dart';
class WaveTile extends StatelessWidget {
const WaveTile({
Key? key,
required this.poster,
required this.wave,
this.extendBelow = false,
}) : super(key: key);
final User poster;
final Wave wave;
final bool extendBelow;
#override
Widget build(BuildContext context) {
User user =
(BlocProvider.of<ProfileBloc>(context).state as ProfileLoaded).user;
return IntrinsicHeight(
child: Row(
children: [
waveColumn(context),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
waveHeader(poster, wave, context, user),
waveText(wave, context),
if (wave.imageUrl != null) waveImage(wave, context),
waveButtons(wave),
],
))
],
),
);
}
Expanded waveColumn(BuildContext context) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 40,
height: 40,
child: InkWell(
child: Hero(
tag: 'wave${wave.id}',
child: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(poster.imageUrls[0])),
),
onTap: () {
BlocProvider.of<vote.VoteBloc>(context)
.add(vote.LoadUserEvent(user: poster));
Navigator.pushNamed(
context,
'/votes',
);
},
),
),
if (extendBelow)
Expanded(
child: VerticalDivider(
color: Color.fromARGB(255, 207, 207, 207),
thickness: 2,
width: 10,
),
),
//add a grey line
]),
);
}
}
Widget waveHeader(User poster, Wave wave, BuildContext context, User user) {
return Row(
children: [
Container(
margin: const EdgeInsets.only(right: 5.0),
child: Text(poster.name,
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontWeight: FontWeight.bold)),
),
Text(
'${poster.handle} · ${timeago.format(wave.createdAt)}',
style: Theme.of(context).textTheme.subtitle1,
),
Spacer(),
WaveTilePopup(poster: poster, wave: wave, user: user),
],
);
}
Widget waveText(Wave wave, BuildContext context) {
return Flexible(
child: TextSplitter(
wave.message,
context,
Theme.of(context).textTheme.subtitle2!,
));
}
Widget waveImage(Wave wave, BuildContext context) {
return Flexible(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
child: CachedNetworkImage(
imageUrl: wave.imageUrl!,
fit: BoxFit.fill,
),
onTap: () {
Navigator.pushNamed(
context,
MyPhotoView.routeName,
arguments: {
'imageUrls': [wave.imageUrl!],
'index': 0
},
);
},
),
),
);
}
Widget waveButtons(Wave wave) {
return BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, profileState) {
if (profileState is ProfileLoading) {
return Container();
}
if (profileState is ProfileLoaded) {
User user = profileState.user;
return Container(
margin: const EdgeInsets.only(top: 10.0, right: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
//font awesome comment icon
Icon(
FontAwesomeIcons.comment,
size: 14.0,
color: Colors.grey,
),
SizedBox(width: 5),
Text(wave.comments.toString()),
],
),
BlocBuilder<WaveLikingBloc, WaveLikingState>(
builder: (context, waveLikingState) {
//set waveLikingState to be WaveLikingLoaded
waveLikingState = waveLikingState as WaveLikingLoaded;
bool inShortTermLikes =
waveLikingState.shortTermLikes.contains(wave.id);
bool inShortTermDislikes =
waveLikingState.shortTermDislikes.contains(wave.id);
bool inLikes = waveLikingState.likes.contains(wave.id);
bool inDislikes = waveLikingState.dislikes.contains(wave.id);
bool likedBy = wave.likedBy.contains(user.id);
bool isLiked = ((likedBy || inLikes || inShortTermLikes) &&
!(inDislikes || inShortTermDislikes));
int likeCount = (inLikes || inShortTermLikes)
? wave.likes + 1
: (inDislikes || inShortTermDislikes)
? wave.likes - 1
: wave.likes;
return LikeButton(
isLiked: isLiked,
size: 30,
circleColor: CircleColor(
start: Colors.red[300]!, end: Colors.red[900]!),
bubblesColor: BubblesColor(
dotPrimaryColor: Colors.red[300]!,
dotSecondaryColor: Colors.red[900]!,
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.red[900] : Colors.grey,
size: 20,
);
},
likeCount: likeCount,
countBuilder: (int? count, bool isLiked, String text) {
var color = isLiked ? Colors.red[900] : Colors.grey;
Widget result;
result = Text(
text,
style: TextStyle(color: color),
);
return result;
},
onTap: (bool isLiked) async {
(isLiked)
? BlocProvider.of<WaveLikingBloc>(context).add(
DislikeWave(waveId: wave.id, userId: user.id!))
: BlocProvider.of<WaveLikingBloc>(context)
.add(LikeWave(waveId: wave.id, userId: user.id!));
return !isLiked;
},
);
},
),
],
),
);
}
return Container();
},
);
}
When creating a wave with just text, it will end up looking like this:
This is the ideal situation right now. However, when an image is added, it looks like this:
Going through widget inspector does not seem to help me much, and the only way I can change the size of the wave is by deleting the image, leading me to believe the image is causing some weird interaction with the Flexible/Expanded widgets near it. Anyone got any ideas?
Thanks!
As you have 2 Flexible in a column , it will take up all the space. You can try removing Flexible from Text.
I am making a Fast Reading exercise app with flutter. I tried run foreach loop on my list to update TextSpans opacity but it didn't work. After that, I tried to replace the element with a new TextSpan and a new opacity, element changed inside the list but it didn't render on screen. Here is my code.
var textList = <TextSpan>[];
#override
void initState() {
super.initState();
text.split(' ').forEach((element) {
textList.add(TextSpan(text:element));
});
}
Widget _textArea() {
return FractionallySizedBox(
widthFactor: 0.9,
heightFactor: 0.9,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 1,
),
),
child: RichText(
text: TextSpan(
children: textList
),
)
));
}
I need to update the opacities of each element inside textList every second and the update function has to start with ``ònPress```event.
I am stuck on that issue.
FULL CODE
import 'package:flutter/material.dart';
class HomePeageReaderWidget extends StatefulWidget {
const HomePeageReaderWidget({Key? key}) : super(key: key);
#override
_HomePageReaderWidgetState createState() => _HomePageReaderWidgetState();
}
class _HomePageReaderWidgetState extends State<HomePeageReaderWidget> {
var text =
"""Gaziantep şehri Türkiye'nin güneydoğusundadır. Çok güzel bir şehirdir. Belki İstanbul ve Ankara kadar büyük değildir, ama yine de burada bir milyonun üstünde insan yaşıyor. Gaziantep'te iş alanları çok geniştir, herkes çalışıyor. Birçok fabrikalar ve atölyeler vardır. Ayrıca bir de Gaziantep Üniversitesi vardır. Üniversite şehirden biraz uzak, ama yurt da üniversitenin yanında. Bunun için ulaşım sorunu yok.Gaziantep'te yazın hava çok sıcak oluyor, insanlar evlerinden ve iş yerlerinden dışarıya çıkmıyorlar. Gece balkonlarda yatıyorlar. Kışın ise havalar sert, sokaklar ise her zaman ıslaktır.""";
var textList = <TextSpan>[];
#override
void initState() {
super.initState();
text.split(' ').forEach((element) {
textList.add(TextSpan(text: element));
});
}
#override
Widget build(BuildContext context) {
Widget _controlRow() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.skip_previous),
iconSize: 50,
color: const Color(0xFFffcd05),
)
],
),
Column(
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.pause_circle),
iconSize: 50,
color: const Color(0xFF91268e),
)
],
),
Column(
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.skip_next),
iconSize: 50,
color: const Color(0xFFffcd05),
)
],
),
],
);
}
Widget _textArea() {
return FractionallySizedBox(
widthFactor: 0.9,
heightFactor: 0.9,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 1,
),
),
child: RichText(
text: TextSpan(children: textList),
)));
}
return Column(
children: [
Expanded(
flex: 6,
child: _textArea(),
),
Expanded(
child: _controlRow(),
),
Container(
width: 300,
child: ElevatedButton.icon(
onPressed: () {
// Respond to button press
},
icon: Icon(Icons.play_arrow, size: 18),
label: Text("Hızlı Egzersiz"),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(const Color(0xFF075098)),
),
),
)
],
);
}
}
You need to use setState( ) so your changes are reflected in the client.
Dont forget that you need to keep a StatefulWidget so that setState works
Code:
onPressed: () {
setState(() {
yourOpacityValue = updatedOpacityValue; });
},
I have a small code for a shopping cart counter in my app, when running the app it does not update upon pressing the add or remove button (+ & - icons), although I assigned the functions for both of them, no errors are shown as to why this is happening...
This is the code for the counter:
import 'package:flutter/material.dart';
class CartCounter extends StatefulWidget {
#override
_CartCounterState createState() => _CartCounterState();
}
class _CartCounterState extends State<CartCounter> {
int numOfItems = 0;
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
buildOutlineButton(
icon: Icons.remove,
press: () {
if (numOfItems > 0) {
setState(() {
numOfItems--;
});
}
},
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: kDefaultPaddin / 2),
child: Text(
// if our item is less then 10 then it shows 01 02 like that
numOfItems.toString().padLeft(2, "0"),
style: Theme.of(context).textTheme.headline6,
),
),
buildOutlineButton(
icon: Icons.add,
press: () {
if (numOfItems < 10) {
setState(() {
numOfItems++;
});
}
}),
],
);
}
SizedBox buildOutlineButton(
{required IconData icon, required Function press}) {
return SizedBox(
width: 40,
height: 32,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
padding: EdgeInsets.zero,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(13.0)),
),
onPressed: press(),
child: Icon(icon),
),
);
}
}
And this is the code where I call the Cart Counter class, it also has a rating bar that works perfectly fine:
class CounterWithRateBar extends StatefulWidget {
#override
_CounterWithRateBarState createState() => _CounterWithRateBarState();
}
class _CounterWithRateBarState extends State<CounterWithRateBar> {
// const CounterWithRateBar({
// Key? key,
// }) : super(key: key);
late double _rating;
int _ratingBarMode = 1;
double _initialRating = 2.0;
IconData? _selectedIcon;
#override
void initState() {
super.initState();
_rating = _initialRating;
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CartCounter(),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children:
<Widget>[
SizedBox(
height: 10.0,
),
_ratingBar(_ratingBarMode),
SizedBox(height: 5.0),
Text(
'Rating: $_rating',
style: TextStyle(fontWeight: FontWeight.bold),
),
]
),
],
);
}
Widget _ratingBar(int mode) {
return RatingBar.builder(
initialRating: _initialRating,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
unratedColor: Colors.amber.withAlpha(50),
itemCount: 5,
itemSize: 25.0,
itemPadding: EdgeInsets.symmetric(horizontal: 2.0),
itemBuilder: (context, _) => Icon(
_selectedIcon ?? Icons.star,
color: Colors.amber,
),
onRatingUpdate: (rating) {
setState(() {
_rating = rating;
});
},
updateOnDrag: true,
);
}
}
You have small mistake in buildOutlineButtonDefinition
You immediately calling press function and not using is as callback;
From
onPressed: press(),
To
onPressed: () => press(),
Full definition would be
SizedBox buildOutlineButton(
{required IconData icon, required Function press}) {
return SizedBox(
width: 40,
height: 32,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
padding: EdgeInsets.zero,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(13.0)),
),
onPressed: () => press(),
child: Icon(icon),
),
);
}
The main reason i want to change the design is that when i scan a product and then scan the same one i get the same product twice while it is the same one so basically i just need to increase the quantity of the product by one.
I have used Provider before but I still can't manage to arrange the classes the right way to make it work.
first i have tried to make a Productobject(which extenes ChangeNotifier) inside ProductCard class and use provider.of(context) and retrieve the quantity of the prodcut.
plus,at the main before the material I used ChangeNotifierProvider
I have a mess in my head and i would love to see someone arranging the code with explanation of how the things works
main
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sally_smart/utilities/product_notifier.dart';
import 'screens/checkout_screen.dart';
import 'screens/login_screen.dart';
import 'screens/registration_screen.dart';
import 'screens/welcome_screen.dart';
import 'package:provider/provider.dart';
//void main() => runApp(Sally());
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(new Sally());
});
}
class Sally extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
backgroundColor: Colors.teal,
cardColor: Color(0xFF068194),
),
initialRoute: LoginScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
LoginScreen.id: (context) => LoginScreen(),
CheckoutScreen.id: (context) => CheckoutScreen()
},
);
}
}
ProductCard
import 'package:flutter/material.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/round_icon_button.dart';
class ProductCard extends StatefulWidget {
final Product prodcut;
ProductCard(Product product);
#override
_ProductCardState createState() => _ProductCardState();
}
class _ProductCardState extends State<ProductCard> {
double finalPrice;
// static int quantity = 1;
#override
Widget build(BuildContext context) {
finalPrice = widget.prodcut.quantity * widget.prodcut.productPrice;
return Card(
elevation: 5.0,
child: ListTile(
leading: Padding(
padding: EdgeInsets.only(
left: 2.0,
),
child: Icon(
widget.prodcut.productIcon,
size: 35,
),
),
title: Text(
widget.prodcut.productName,
style: kProductNameTextStyle,
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RoundIconButton(
icon: Icons.add,
color: Colors.green,
function: () {
setState(() {
widget.prodcut.quantity++;
});
}),
Text(
'$widget.prodcut.quantity',
),
RoundIconButton(
icon: Icons.remove,
color: Colors.red,
function: () {
setState(() {
widget.prodcut.quantity--;
if ( widget.prodcut.quantity == 0) {
widget.prodcut.quantity++;
}
});
}),
],
),
),
Text(
'${finalPrice.toStringAsFixed(2)} ₪',
style: TextStyle(fontSize: 15),
),
],
),
),
);
}
}
Prodcut
import 'package:flutter/cupertino.dart';
class Product extends ChangeNotifier{
String productName;
double productPrice;
IconData productIcon;
String id;
String barCode;
int quantity;
Product(this.productName, this.productPrice, this.productIcon, this.id,
this.barCode,this.quantity);
}
welcome screen
import 'package:audioplayers/audio_cache.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:sally_smart/screens/login_screen.dart';
import 'package:sally_smart/screens/registration_screen.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/product_card.dart';
import 'package:sally_smart/utilities/scan_button_const.dart';
import 'package:sally_smart/utilities/scan_methods.dart';
//import 'package:sally_smart/utilities/scan_pageML.dart';
//import 'package:flutter_camera_ml_vision/flutter_camera_ml_vision.dart';
//import 'package:firebase_ml_vision/firebase_ml_vision.dart';
//working version
//List<ProductCard> shoppingList = [];
final sallyDatabase = Firestore.instance;
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
final _auth = FirebaseAuth.instance;
final textEditorController = TextEditingController();
String _scanBarcode = 'Unknown';
String productName = 'Product Test';
double productPrice;
String productBarCode;
IconData productIcon = Icons.add_shopping_cart;
final List<ProductCard> shoppingList = [];
int productId = 0;
static AudioCache barcodeSound = AudioCache();
// saves barcodes data
List<String> data = [];
Future<void> initPlatformState() async {
String barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes =
await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_scanBarcode = barcodeScanRes;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF21bacf),
appBar: AppBar(
elevation: 3,
backgroundColor: Colors.black54,
leading: Icon(
Icons.shopping_basket,
size: 30,
),
title: Text(
'Sally',
textAlign: TextAlign.end,
style: kHeaderTextStyle,
),
actions: <Widget>[
IconButton(icon: Icon(Icons.settings), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(
icon: Icon(Icons.power_settings_new),
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, LoginScreen.id);
}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(icon: Icon(Icons.share), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
// PopupMenuButton(itemBuilder: ),
],
),
body: Container(
decoration: kBackgroundGradientScan,
child: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
'ברוכה הבאה, סאלי',
textAlign: TextAlign.right,
style: kHeaderTextStyle,
),
Padding(
padding: EdgeInsets.only(top: 5, right: 15),
child: Hero(
tag: 'Sally',
child: CircleAvatar(
backgroundImage:
AssetImage('images/missing_avatar_F.png'),
maxRadius: 25,
),
),
)
],
),
Padding(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 15),
child: TextField(
textAlign: TextAlign.center,
onChanged: (value) async {
_scanBarcode = value;
productPrice = await getProductPrice(_scanBarcode);
productName = await getProductName(_scanBarcode);
},
decoration: kTextFieldDecoration.copyWith(
prefixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {
_scanBarcode = '';
textEditorController.clear();
try {
setState(() {
textEditorController.clear();
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
} catch (e) {
print(e);
}
},
),
// prefix: IconButton(
// icon: Icon(Icons.search),
// onPressed: () {
//
// }),
hintText: '...הכנס ברקוד או שם מוצר ידנית'),
),
),
DividerSally(),
shoppingListBuilder(),
DividerSally(),
Container(
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
ScanMainButton(
iconData: Icons.flip,
buttonText: 'סרוק מוצר',
onPressed: () async {
//Navigator.pushNamed(context, ScanScreen.id);
await initPlatformState();
barcodeSound.play('barcode_sound.mp3');
//Changes the product name by referencing to the database
productBarCode = _scanBarcode;
productPrice =
await getProductPrice(_scanBarcode);
productName =
await getProductName(_scanBarcode);
setState(() {
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
},
color: Colors.teal,
),
ScanMainButton(
iconData: Icons.check,
buttonText: 'מעבר לתשלום',
color: Colors.green,
onPressed: () {
Navigator.pushNamed(
context, RegistrationScreen.id);
}),
],
),
),
),
)
],
),
),
),
));
}
Expanded shoppingListBuilder() {
return Expanded(
child: Container(
color: Colors.black38,
child: ListView.builder(
reverse: true,
itemCount: shoppingList.length,
itemBuilder: (context, index) {
ProductCard item = shoppingList[index];
return Dismissible(
key: Key(item.prodcut.id),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
setState(() {
shoppingList.removeAt(index);
});
},
background: Container(
child: Icon(
Icons.restore_from_trash,
size: 40,
),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [
Color(0x8C650223),
Color(0x8CB9013E),
],
stops: [0.1, 0.9],
),
),
),
child: item //ListTile(title: Text('${item.productName}.')),
);
},
),
));
}
}
I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.