I am trying to rebuild a stateful widget every time a value in my global Singelton is changed but I'm stumped.
My goal is to rebuild my Cart Icon every time the cartSize is changed throughout my app.
I know I need to send out a notification whenever the Singelton cartSize value is changed. and listen for that notification in my stateful widget but how do I do this?
Any help is greatly appreciated.
My Global Singelton
library #######.globals;
import 'package:flutter/material.dart';
class GlobalSingleton extends ChangeNotifier {
static final GlobalSingleton _instance = GlobalSingleton._internal();
// passes the instantiation to the _instance object
factory GlobalSingleton() {
return _instance;
}
//initialize variables in here
GlobalSingleton._internal() {
cartSize = 0;
}
late int cartSize;
}
My stateful Widget
import 'package:######/globals/globals.dart';
import 'package:flutter/material.dart';
class BuildMarketplaceCartIcon extends StatefulWidget {
const BuildMarketplaceCartIcon({Key? key}) : super(key: key);
#override
State<BuildMarketplaceCartIcon> createState() =>
_BuildMarketplaceCartIconState();
}
class _BuildMarketplaceCartIconState extends State<BuildMarketplaceCartIcon> {
CRUDMarketplaceCart localCartData = CRUDMarketplaceCart();
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
},
child: Container(
width: 72,
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Stack(
alignment: Alignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Icon(Icons.shopping_cart),
Text(
'Cart',
overflow: TextOverflow.ellipsis,
),
],
),
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
alignment: Alignment.center,
child: Text(
'${globals.GlobalSingleton().cartSize}',
),
),
),
],
),
),
);
}
}
When using ChangeNotifier you need to call notifyListeners when the value changes. You also need to have your widget listen to the ChangeNotifier, so that it knows when to rebuild.
The most common way to go about that is to use the provider package, which includes the ChangeNotifierProvider widget.
Using provider, your code would look something like this:
class GlobalSingleton extends ChangeNotifier {
static final GlobalSingleton _instance = GlobalSingleton._internal();
// passes the instantiation to the _instance object
factory GlobalSingleton() {
return _instance;
}
//initialize variables in here
GlobalSingleton._internal() {
_cartSize = 0;
}
late int _cartSize;
int get cartSize => _cartSize;
void set cartSize(int newCartSize) {
_cartSize = newCartSize;
notifyListeners();
}
}
Here, we update your singleton so that it will call notifyListeners() whenever the cartSize is set.
Next you'll need to update your widget to listen to the changes:
import 'package:######/globals/globals.dart';
import 'package:flutter/material.dart';
class BuildMarketplaceCartIcon extends StatefulWidget {
const BuildMarketplaceCartIcon({Key? key}) : super(key: key);
#override
State<BuildMarketplaceCartIcon> createState() =>
_BuildMarketplaceCartIconState();
}
class _BuildMarketplaceCartIconState extends State<BuildMarketplaceCartIcon> {
CRUDMarketplaceCart localCartData = CRUDMarketplaceCart();
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {},
child: Container(
width: 72,
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Stack(
alignment: Alignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Icon(Icons.shopping_cart),
Text(
'Cart',
overflow: TextOverflow.ellipsis,
),
],
),
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
alignment: Alignment.center,
child: ChangeNotifierProvider.value(
value: GlobalSingleton(),
child: Consumer<GlobalSingleton>(
builder: (context, singleton, child) {
return Text(
'${singleton.cartSize}',
);
},
),
),
),
),
],
),
),
);
}
}
Here I put the Provider widget as directly enclosing the Consumer widget - however, if you need the singleton's value in more than one place, you can put the Provider higher up the widget tree so that it's a common ancestor of any Consumer that listens to changes in the singleton.
Related
I have one login page .dart that contains 3 seperate object class dart such as : InputEmail , Password and ButtonLogin which its split each other but it's called together in login page
My problem is how can i validate form input email and password when i submit the button login when field is empty and email not valid
I tried to create Globalkey FormState inside login page and call it on button login class dart though
Onpressed event but nothing give me error message.
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.redAccent, Colors.lightBlueAccent]),
),
key: _formKey,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Row(children: const <Widget>[
VerticalText(),
TextLogin(),
]),
const InputEmail(),
const PasswordInput(),
const ButtonLogin(),
const FirstTime(),
],
),
],
),
),
);
}
}
class ButtonLogin extends StatefulWidget {
const ButtonLogin({Key? key}) : super(key: key);
#override
_ButtonLoginState createState() => _ButtonLoginState();
}
class _ButtonLoginState extends State<ButtonLogin> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40, right: 50, left: 200),
child: Container(
alignment: Alignment.bottomRight,
height: 50,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: Colors.blue,
blurRadius: 10.0, // has the effect of softening the shadow
spreadRadius: 1.0, // has the effect of extending the shadow
offset: Offset(
5.0, // horizontal, move right 10
5.0, // vertical, move down 10
),
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FlatButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'OK',
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
Icon(
Icons.arrow_forward,
color: Colors.lightBlueAccent,
),
],
),
),
),
);
}
}
You can pass the callback of onPressed function of button to Login Page and validate it there.
class ButtonLogin extends StatefulWidget {
const ButtonLogin({Key? key, required this.onPressed}) : super(key: key);
final VoidCallBack onPressed ;
#override
_ButtonLoginState createState() => _ButtonLoginState();
}
class _ButtonLoginState extends State<ButtonLogin> {
#override
Widget build(BuildContext context) {
return Padding(
child: Container(
....
child: FlatButton(
onPressed: widget.onPressed,
child: Row(
...
);
}
}
and change add this onPressed parameter inside ButtonLogin widget on Login page
const InputEmail(),
const PasswordInput(),
const ButtonLogin(onPressed: () {
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},),
const FirstTime(),
The easy way is to create a single StateFulWidget and within the StateFulWidget, you brake your form Widgets (InputEmail, Password and ButtonLogin) into separate Widget methods(functions). Afterward Wrap them with a Form widget using Row, Column or any Widget that accept list.
Next you add the _formKey to the Form widget. I will advice the use of controller for your inputs. Below is an example using your code. By the way change FlatButton -> TextButton
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.redAccent, Colors.lightBlueAccent]),
),
child: Form(
key: _formKey,
child: ListView(
children: [
Column(
children: [
// inputEmail(),
// passwordInput(),
buttonLogin(),
],
),
],
),
),
),
);
}
Widget buttonLogin() {
return Padding(
padding: const EdgeInsets.only(top: 40, right: 50, left: 200),
child: Container(
alignment: Alignment.bottomRight,
height: 50,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: const [
BoxShadow(
color: Colors.blue,
blurRadius: 10.0,
spreadRadius: 1.0,
offset: Offset(5.0, 5.0),
),
],
),
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'OK',
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
Icon(
Icons.arrow_forward,
color: Colors.lightBlueAccent,
),
],
),
),
),
);
}
}
I have an application like this:
My aim is that when I press the eye icon next to the text "Hello", I want a box to open just below the text and write the German version of "Hello". So it will say "Hallo".
My purpose is to show the meaning of the word.
When I press the eye, I want to show the German of the word. How can I make a white box under the word Hello, that is, the box in which the German language will be written?
Codes:
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
class selamlasmaLearn extends StatelessWidget {
List <wordAndMeaning> wordsList = [wordAndMeaning("Hello", "Hallo"), wordAndMeaning("Go", "Gehen")];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) {
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
word.word,
style: TextStyle(fontSize: 45, color: Colors.white),
),
SizedBox(width: 10,),
Icon(Icons.remove_red_eye_sharp, color: Colors.white, size: 25,), // <<<<<<<<<
],
),
),
);
},
);
}).toList(),
);
}
),
);
}
}
class wordAndMeaning {
String word;
String meaning;
wordAndMeaning(this.word, this.meaning);
}
I keep the word and its German in a list called wordsList.
Thanks for the help in advance.
You can convert the widget to StatefulWidget or use a ValueNotifier to control the preserve/notify the state visibility.
You can use Visibility widget or just if to show and hide German text.
class selamlasmaLearn extends StatefulWidget {
#override
State<selamlasmaLearn> createState() => _selamlasmaLearnState();
}
class _selamlasmaLearnState extends State<selamlasmaLearn> {
bool _showGerman = false;
List<wordAndMeaning> wordsList = [
wordAndMeaning("Hello", "Hallo"),
wordAndMeaning("Go", "Gehen")
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (context) {
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(word.word,
style:
TextStyle(fontSize: 45, color: Colors.white)),
if (_showGerman) Text(word.meaning), //modify the way you want
],
),
const SizedBox(
width: 10,
),
IconButton(
icon: Icon(Icons.remove_red_eye_sharp),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
_showGerman = !_showGerman;
});
},
),
],
),
);
},
);
}).toList(),
);
}),
);
}
}
Use the Tooltip widget
I'm emphasizing on the popup part in your question title. When using a Tooltip you ensure that your widgets do not shift position or jump when the Tooltip widget appear, as the example below illustrates.
Example code:
import 'package:flutter/material.dart';
class TooltipExample extends StatelessWidget {
const TooltipExample({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Tooltip(
// Set the tooltip to trigger on a single tap, tapping outside the
// widget will make the tooltip disappear.
triggerMode: TooltipTriggerMode.tap,
// The message shown when the tooltip appears.
message: "Tooltip showing!",
// Consider adjusting this to your needs.
showDuration: const Duration(days: 1),
// The widget that must be clicked to show the tooltip.
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text("Hello"),
SizedBox(
width: 8,
),
Icon(Icons.visibility),
],
),
),
),
const Padding(
padding: EdgeInsets.all(8.0),
child: Text("Cover me!"),
)
],
),
);
}
}
// Some code to run the above example, note the theme part that turns the
// tooltip white.
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
// Style the overall design of tooltips in the app in one place,
// or provide in each tooltip individually.
theme: ThemeData(
tooltipTheme: const TooltipThemeData(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(4),
),
),
textStyle: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
),
),
),
home: const Scaffold(
backgroundColor: Colors.amber,
body: TooltipExample(),
),
);
}
}
void main() => runApp(const App());
Here is how it looks:
Note that the Tooltip widget overlays whatever is below it. (instead of pushing it further down - like toggling the visibility of a normal widget in a row or column would have done)
My issue is what the title says - I have a gridview of flip cards. When I flip a few over, scroll past them and then scroll back up the cards have flipped back. I don't really want that to happen because it's supposed to be that every time the user flips a card a point is added to a total, and then when they flip it back a point is taken away from the total. I've tried "AutomaticKeepAliveClientMixin", which works to preserve the state when I have another tab, but it doesn't seem to help keep the cards flipped when I scroll.
Apologies in advance if an obvious solution exists here and I've missed it. I did try to find a solution online.
Here is the code from the dart file I'm working with:
import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'statenames.dart';
import 'globalVariables.dart';
StateNames stateObject = new StateNames();
class GridOne extends StatefulWidget {
final Function updateCounter;
final Function decreaseCount;
GridOne(this.updateCounter, this.decreaseCount);
#override
_GridOneState createState() => _GridOneState();
}
class _GridOneState extends State<GridOne>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
int points = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: GridView.count(
crossAxisCount: 4,
children: List.generate(52, (index){
return Card(
elevation: 0.0,
margin: EdgeInsets.only(left: 3.0, right: 3.0, top: 9.0, bottom: 0.0),
color: Color(0x00000000),
child: FlipCard(
direction: FlipDirection.HORIZONTAL,
speed: 1000,
onFlipDone: (status) {
setState(() {
(status)
? widget.decreaseCount()
: widget.updateCounter();
});
print(counter);
},
front: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FittedBox(fit:BoxFit.fitWidth,
child: Text(stateObject.stateNames[index], style: TextStyle(fontFamily: 'Architects Daughter', color: Colors.white), )
),
Text('',
style: Theme.of(context).textTheme.body1),
],
),
),
back: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage(stateObject.licensePlatePaths[index])),
],
),
),
),
);
})
),
);
}
}
Thank you so much for reading.
I did hit trial to achieve what you wanted and i think it's only possible if you use column. I even tried using simple ListView but still the the card will return to front face
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Test(),
);
}
}
class Test extends StatefulWidget{
#override
_Test createState() => _Test();
}
class _Test extends State<Test>{
List<Widget> list = [];
_Test(){
list = new List.filled(30, flipCards());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: list,
),
)
);
}
Widget flipCards(){
return Container(
height: 70,
child: FlipCard(
flipOnTouch: true,
front: Card(
child: Container(
alignment: Alignment.center,
child: Text('Toggle'),
),
),
back: Card(
child: Container(
alignment: Alignment.center,
child: Text('Back'),
),
),
)
);
}
}
You need to use the AutomaticKeepAliveClientMixin on the children of the GridView
So make a new stateful widget for your cards and use the keepalive there.
Also for the AutomaticKeepAliveClientMixin you need to call super.build(context); in your build method
I have a main dart file and a settings dart file. The settings dart file is responsible for the appearance of the main dart file. Settings dart has a AppTheme class. Upon users typing on this settings page I want the main page to update.
My attempt at this was calling the class and redefining the variables based on user input. Doesnt work whether or not I use setState(). I also tried jus staying on the main page and tried changing the theme onPressed for the settings button. That didnt work either. Iconbutton update doesnt update the state either.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:resume/settings.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
theme: ThemeData.light(),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
double buttonMargin = MediaQuery.of(context).size.width / 10;
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return MaterialApp(
home: Scaffold(
backgroundColor: AppTheme().backgroundMain,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Column(
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height / 2.25),
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.all(10),
child: Text(AppTheme().name,
style: TextStyle(
fontFamily: 'Pacifico',
fontSize: screenWidth / 8.57142857143,
color: AppTheme().nameTextColor,
fontWeight: FontWeight.bold)),
),
]),
),
Container(
height: screenHeight / 2.3,
alignment: Alignment.bottomCenter,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(
buttonMargin * 1.5,
buttonMargin * 3,
buttonMargin * 1.5,
buttonMargin * 3),
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
child: RaisedButton(
color: AppTheme().backgroundSecondary,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.settings,
color: Colors.white,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Settings',
style: TextStyle(color: Colors.white),
),
)
],
),
onPressed: () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return MySettings();
}),
);
},
);
},
),
),
],
),
),
],
),
),
),
),
);
}
}
settings.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
void settings() {
runApp(MySettings());
}
class AppTheme {
var backgroundMain = Colors.red;
var backgroundSecondary = Colors.teal;
var backgroundAvatar = Colors.white;
var nameTextColor = Colors.white;
var professionTextColor = Colors.red[100];
var contactTextColor = Colors.teal;
var testPrint = print("hi");
String name = 'John Doe';
String nameFont = 'Pacifico';
String professionFont = 'roboto';
}
class MySettings extends StatefulWidget {
#override
MySettingsState createState() => MySettingsState();
}
class MySettingsState extends State<MySettings> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: AppTheme().backgroundMain,
title: Text('Settings'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
setState(() {
AppTheme().backgroundMain = Colors.yellow;
print(AppTheme().name);
Navigator.pop(context);
});
},
),
),
backgroundColor: Colors.teal,
body: SafeArea(
child: ListView(
children: <Widget>[
Container(
child: TextField(
onChanged: (text) {
setState(() {
AppTheme().name = text;
});
},
),
),
Container(
child: TextField(),
),
],
)),
),
);
}
}
1 Make sure to properly import
Flutter/Dart Static variables lost / keep getting reinitialized
2 You cannot alter the variables in the class. You must create an object and alter the object.
i.e I was doing
Class MyClass {
var myVariable = someValue
}
MyClass.myVariale = aDifferentValue
This did not update MyClass
What I needed to do worked once I created an object
var myClassObject = new Myclass();
myClassObject.myVariable = aDifferentValue
Now I just alter and call on myClassObject.
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;
});
},
);
}
}