I have this flutter code.
When I select new item from dropdown list, the value of _selectedCurrency is updated, but the dropdown button itself not updated. The item shown is always USD.
import 'package:flutter/material.dart';
import 'coin_data.dart' as coinData;
class PriceScreen extends StatefulWidget {
#override
_PriceScreenState createState() => _PriceScreenState();
}
class _PriceScreenState extends State<PriceScreen> {
String _selectedCurrency = "USD";
DropdownButton _currencyDropdownButton;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Coin Ticker'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: _currencyDropdownButton,
),
],
),
);
}
#override
void initState() {
super.initState();
_currencyDropdownButton = DropdownButton<String>(
value: _selectedCurrency,
items:
coinData.currenciesList.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedCurrency = value;
});
},
);
}
}
But if I created the DropdownButton widget inside build() then everything works fine, like this
import 'package:flutter/material.dart';
import 'coin_data.dart' as coinData;
class PriceScreen extends StatefulWidget {
#override
_PriceScreenState createState() => _PriceScreenState();
}
class _PriceScreenState extends State<PriceScreen> {
String _selectedCurrency = "USD";
DropdownButton _currencyDropdownButton;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Coin Ticker'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: DropdownButton<String>(
value: _selectedCurrency,
items: coinData.currenciesList
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedCurrency = value;
});
},
),
),
],
),
);
}
}
I'm trying to make my code neat, by not create everything on build().
Is creating the widget on initState() is correct way in flutter?
Thanks
why would you want to put your widget inside initState()? in rebuild progress, only widgets inside build() will get updated but you created an immutable widget inside init.
I offer you using the provider package. create a new StatelessWidget class and use ChangeNotifierProvider<String>() in root
to rebuild your widget on every change.
No initState is called only once, while building, you can not change if you declare inside, it might possible with GlobalKey only.try with GlobalKey.
and when setState(); , is called build method get update. so we have to put widget inside build method only.
Related
I am trying the code put by Resocoder for integrating the Hive with Flutter. Everything was going fine but i got stuck at a place from where i cant figure out what to do. If you see contactsbox is throwing error stating that itsa n object and i cant use lingth property on it or even any other property. I have market the partof the code in bold throwing error. Any idea why is this error happening. HOw shall i use contactsBox as List unable to understand it. Any help would be great
contact_page.dart
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'new_contact_form.dart';
import 'contact.dart';
class ContactPage extends StatelessWidget {
const ContactPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hive Tutorial'),
),
body: Column(
children: [
Expanded(
child: _buildListView(),
),
NewContactForm(),
],
),
);
}
Widget _buildListView() {
return ValueListenableBuilder(
valueListenable: Hive.box('contacts').listenable(), builder:
(context, contactsBox, _) {
return ListView.builder(**itemCount: contactsBox.length** , itemBuilder: (context, index) {
final contact = contactsBox.getAt(index) as Contact;
return ListTile(
title: Text(contact.name!),
subtitle: Text(contact.age.toString()),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
contactsBox!.putAt(
index,
Contact('${contact.name}*', contact.age + 1),
);
},
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
contactsBox.deleteAt(index);
},
)
],
),
);
}
);
}
);
}
new_contact_form.dart
import 'package:db_app/contact.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
class NewContactForm extends StatefulWidget {
const NewContactForm({Key? key}) : super(key: key);
#override
State<NewContactForm> createState() => _NewContactFormState();
}
class _NewContactFormState extends State<NewContactForm> {
final _formKey = GlobalKey<FormState>();
String? _name;
String? _age;
void addContact(Contact cnt) {
final contactBox = Hive.box('contacts');
contactBox.add(cnt);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Name'),
onSaved: (value) => _name = value,
),
SizedBox(height: 10),
TextFormField(
decoration: InputDecoration(labelText: 'Age'),
onSaved: (value) => _age = value,
keyboardType: TextInputType.number,
),
ElevatedButton(
onPressed: () {
_formKey.currentState!.save();
final newContact = Contact(_name, int.parse(_age!));
addContact(newContact);
},
child: Text('Add New Contact'),
),
],
),
));
}
}
I am facing a strange bug when I try to add a dynamic widget to my app. When press add button screen turns completely white I can not find why it happens.
I use https://www.youtube.com/watch?v=xPW1vtDDlt4 as resource I am really new at Flutter maybe I forget something to add bu I check many times.
Here is my code,
class DynamicWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: new TextField(
decoration: new InputDecoration(hintText: 'Press + to Add Field'),
),
);
}
}
Initialization of the list.
List<DynamicWidget> listDynamic = [];
My function to add widgets to the list.
addDynamic() {
listDynamic.add(new DynamicWidget());
print("addDynamic");
setState(() {});
}
I am not sure but problem might be here,
final testText = Visibility(
child: new Column(
children: <Widget>[
new Flexible(
child: new ListView.builder(
itemCount: listDynamic.length,
itemBuilder: (_, index) => listDynamic[index],
),
),
],
),
);
Here I call my widget which I declare it to variable here.
final body = Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
testText,
strPhoto
],
),
),
);
And finally my button.
return Scaffold(
appBar: new AppBar(title: new Text(device_type), centerTitle: true),
drawer: Menu(),
body: body,
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
addDynamic();
},
),
],
));
Thanks for helping me.
you must create variable Widget and add to Build Context on Scaffold
In the first step, i created a Widget called CustomTextField
import 'package:flutter/material.dart';
class CustomTextField extends StatelessWidget {
final String hint;
final TextEditingController controllers;
CustomTextField(this.hint, this.controllers);
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: hint),
controller: controllers),
SizedBox(height: 4.0),
],
),
);
}
}
this widget give me one text for display on hint and one controller for control textfield
And in the next step, I change the homepage class this way
i have list of Custom TextField (my widget) and display on listview using mapping list of build method
import 'package:flutter/material.dart';
import 'CustomTextField.dart';
class PageTutorial extends StatefulWidget {
#override
_PageTutorialState createState() => _PageTutorialState();
}
class _PageTutorialState extends State<PageTutorial> {
List<CustomTextField> widgets = [
CustomTextField("UserName", TextEditingController())
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: widgets.map<Widget>((widget) => widget).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widgets.add(CustomTextField("Password", TextEditingController()));
});
},
child: Icon(Icons.add)),
);
}
}
have fun
when i create a switch widget inside a drawer it takes all the width possible to fill the drawer width,
i tried wrapping it with a Container and a sizedBox to decrease the its width but that didn't work.
here is an image that shows the result of my code
app image here
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
bool value = false;
bool newValue = true;
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test App'),
centerTitle: true,
),
drawer: Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
),
ListTile(
title: Text('Tile 1'),
),
SizedBox(
width: 50,
child: Switch(
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
),
)
],
),
),
);
}
}
Here's a fairly simple way to resolve.
Row(
children: <Widget>[
Switch(
value: false,
onChanged: null,
),
Expanded(child: Container(),)
],
),
Hi can you guys help me with this, I know that the Expanded widget requires a Flex parent. but for some reason, mine gives an error:
my code are:
import 'package:flutter/material.dart';
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('BMI CALCULATOR')),
),
body: Column(
children: <Widget>[
Expanded(
child: Container(
child: ReusableCard(colour: Color(0xFF141A3C)),
),
),
],
),
);
}
}
class ReusableCard extends StatelessWidget {
//CUSTOM CONSTRUCTOR
//the color from the Stateful Widget from above is passed in to the INPUT of ReusableCard({PASSED IN HERE}),
ReusableCard({this.colour});
Color colour;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
margin: EdgeInsets.all(15.0),
// height: 200.0,
// width: 170.0,
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(10.0),
),
),
);
}
}
UPDATE
Ive tried adding another Extended widget AS A CHILD of Column() but the error reappears
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('BMI CALCULATOR')),
),
body: Column(
children: <Widget>[
Expanded(
child: Row(
// <------ change this Container to a Row or Column
children: <Widget>[
ReusableCard(colour: Color(0xFF141A3C)),
ReusableCard(colour: Color(0xFF141A3C)),
],
),
),
Expanded(
child: ReusableCard(
colour: Color(0xFF141A3C),
),
)
],
),
);
}
}
Your ReusableCard (which returns an Expanded widget) is wrapped within a Container class. This is what's giving you the error. To fix this, you can simply change your Container class to a Row or Column:
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('BMI CALCULATOR')),
),
body: Column(
children: <Widget>[
Expanded(
child: Container( // <------ change this Container to a Row or Column
child: ReusableCard(colour: Color(0xFF141A3C)
),
),
),
],
),
);
}
}
The problem here is that the Container doesn't redraw itself after changing the funArg value with the press of a button which should change its height since it's used in its calculation
here is the code :
here is main.dart:
import 'package:flutter/material.dart';
import 'package:sqaure/ui/fun.dart';
Widget rect0;
String rectArg = "20";
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new HomeState();
}
}
class HomeState extends State<Home> {
var list = ["20", "15"];
Widget funTest() {
setState(() {
rectArg = list[1];
rect0 = new Plate(rectArg);
});
}
//final Color primaryColor = Colors.red;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("rect"),
backgroundColor: Colors.red,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.topCenter,
color: Colors.white,
height: 245.0,
child: new Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.only(left: 55.0),
child: Row(
children: <Widget>[
//plates
rect0 = new Plate(rectArg),
],
),
),
)
],
),
),
new RaisedButton(onPressed: () {
funTest();
debugPrint(rectArg);
})
],
),
);
}
}
and here is fun.dart:
import 'package:flutter/material.dart';
class Plate extends StatefulWidget {
final String funArg2;
#override
State<StatefulWidget> createState() {
return new PlateState(funArg2);
}
[enter image description here][1]
Plate(this.funArg2);
}
class PlateState extends State<Plate> {
String funArg;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: Container(
alignment: Alignment.center,
color: Colors.redAccent,
height: funArg != "" ? (9.33 * double.parse(funArg) + 45) : 0.0,
width: 29.0,
child: new Text(
funArg,
style: new TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: funArg.length > 4
? 10.0
: funArg.length > 3 ? 14.0 : 19.0,
),
),
));
}
PlateState(this.funArg);
}
as you can see the height of the container is determined by the child text inside.
screenshot
thank you.
Here is a fixed and commented version of your code. Please read the comments!
The main problem is that you defined Plate as a stateful widget, and stored rectArg in the state! PlateState is only initiated once, until you leave the screen, it's not recreated when the parent widget is rebuilt!
Plate actually doesn't have any internal state, so it should be a StatelessWidget. You should always prefer StatelessWidgets. Understanding why is fundamental for Flutter development!
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: Home()));
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new HomeState();
}
}
// this seems to be a constant, so can put it outside of the class
// or alternatively inside, with "static const" modifier
const list = ["20", "15"];
class HomeState extends State<Home> {
// stateful variables (things that change over time)
// must be inside of your state class
String rectArg = "20";
// we can return void here!
void funTest() {
setState(() {
// state is modified here. this triggers a rebuild/redraw
// that means the build function is called again
// note that we are only the storing the string value, NOT a widget!
rectArg = list[1];
});
}
// this is called every time you setState
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("rect"),
backgroundColor: Colors.red,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.topCenter,
color: Colors.white,
height: 245.0,
child: new Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.only(left: 55.0),
child: Row(
children: <Widget>[
// DO NOT SET VARIABLES FROM THE BUILD METHOD!
// this is bad:
// rect0 = new Plate(rectArg),
Plate(
funArg: rectArg,
),
],
),
),
)
],
),
),
new RaisedButton(onPressed: () {
funTest();
debugPrint(rectArg);
})
],
),
);
}
}
// Plate is actually a StatelessWidget because it is not interactive and holds no internal state
// All the data (funArg) is passed in from the parent ==> StatelessWidget
// Always prefer stateless widgets!
// That means the widget is completely rebuilt every time the build() method is called in HomeState
class Plate extends StatelessWidget {
// Use named constructor parameters and call the super constructor!
// you can auto-generate the constructor with Android Studio
const Plate({Key key, this.funArg}) : super(key: key);
final String funArg;
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: Container(
alignment: Alignment.center,
color: Colors.redAccent,
height: funArg != "" ? (9.33 * double.parse(funArg) + 45) : 0.0,
width: 29.0,
child: new Text(
funArg,
style: new TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: funArg.length > 4 ? 10.0 : funArg.length > 3 ? 14.0 : 19.0,
),
),
),
);
}
}
Just in case that you need a StatefulWidget with internal state that also has constructor parameters set by the parent widget (which is quite common): Inside the build method of your State, use the widget property to access the final fields of your widget:
class ColoredCheckbox extends StatefulWidget {
const ColoredCheckbox({Key key, this.color}) : super(key: key);
// this is passed in from the parent, can change when the parent is rebuilt
final Color color;
#override
State<StatefulWidget> createState() => ColoredCheckboxState();
}
class ColoredCheckboxState extends State<ColoredCheckbox> {
// this is internal state, kept even when the parent is rebuilt
bool checked = false;
// build is called when:
// - you call setState from this widget
// - when the parent widget is rebuilt
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text(checked ? 'X' : '0'),
// use "widget" to access the fields passed in from the parent
color: widget.color,
onPressed: () {
// always call setState when changing internal state
setState(() {
checked = !checked;
});
},
);
}
}