I'm using a statefull widget to handle the length of my text. (show more, show less)
class DescriptionTextWidget extends StatefulWidget {
final String text;
DescriptionTextWidget({#required this.text});
#override
_DescriptionTextWidgetState createState() =>
new _DescriptionTextWidgetState();
}
class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
String firstHalf;
String secondHalf;
bool flag = true;
#override
void initState() {
super.initState();
if (widget.text.length > 400) {
firstHalf = widget.text.substring(0, 400);
secondHalf = widget.text.substring(400, widget.text.length);
} else {
firstHalf = widget.text;
secondHalf = "";
}
}
#override
Widget build(BuildContext context) {
return new Container(
padding: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: secondHalf.isEmpty
? new Text(firstHalf, style: TextStyle(color: Colors.white))
: new Column(
children: <Widget>[
new Text(
flag ? (firstHalf + "...") : (firstHalf + secondHalf),
style: TextStyle(color: Colors.white),
),
new InkWell(
splashColor: Colors.transparent,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text(
flag ? "show more" : "show less",
style:
new TextStyle(color: Colors.white.withOpacity(0.8)),
),
],
),
onTap: () {
setState(() {
flag = !flag;
});
},
),
],
),
);
}
}
In my main class: I 'give' the text to that stfull widget like this:
GestureDetector(
onTap: () async {
},
child: DescriptionTextWidget(
text: myString,
),
If I update myString in my main statefull widget, the String doesn't get updated in the statefull widget 'DescriptionTextWidget'.
What's the best way to update the String in the class DescriptionTextWidget?
Thanks in advance!
Sample on DartPad
class DescriptionTextWidget extends StatefulWidget {
final ValueNotifier<String> text;
}
class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
#override
void initState() {
super.initState();
widget.text.addListener(() => setState(initText));
initText();
}
initText() {
if (widget.text.value.length > 400) {
firstHalf = widget.text.value.substring(0, 400);
secondHalf = widget.text.value.substring(400, widget.text.value.length);
} else {
firstHalf = widget.text.value;
secondHalf = "";
}
}
}
main class:
ValueNotifier<String> myString;
updateString(String value){
myString.value = value;
}
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
List<String> myString = ["test"];
void _replace() {
setState(() {
myString[0] = "tapped";
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: DescriptionTextWidget(
text: myString,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _replace,
child: Icon(Icons.autorenew),
),
);
}
}
class DescriptionTextWidget extends StatefulWidget {
final List<String> text;
DescriptionTextWidget({#required this.text});
#override
_DescriptionTextWidgetState createState() =>
new _DescriptionTextWidgetState();
}
class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
bool flag = true;
#override
Widget build(BuildContext context) {
String firstHalf;
String secondHalf;
if (widget.text[0].length > 400) {
firstHalf = widget.text[0].substring(0, 400);
secondHalf = widget.text[0].substring(400, widget.text[0].length);
} else {
firstHalf = widget.text[0];
secondHalf = "";
}
return new Container(
padding: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: secondHalf.isEmpty
? new Text(firstHalf, style: TextStyle(color: Colors.black))
: new Column(
children: <Widget>[
new Text(
flag ? (firstHalf + "...") : (firstHalf + secondHalf),
style: TextStyle(color: Colors.black),
),
new InkWell(
splashColor: Colors.transparent,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text(
flag ? "show more" : "show less",
style:
new TextStyle(color: Colors.black.withOpacity(0.8)),
),
],
),
onTap: () {
setState(() {
flag = !flag;
});
},
),
],
),
);
}
}
Related
I have created a row of 3 buttons and each of them will change the color independently when clicked, each button works independently which means you could make all buttons clicked, but what i want to achieve is that i can only make one button clicked at one time, which means if i click button 2 after clicking button1, button2 will change the state to clicked, and button1 will change the state to unclicked, just like radio buttons. How can this can be done?
Below is the code of the buttons
class _MyHomePageBodyState extends State<MyHomePageBody> {
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Dis(),
SizedBox(width:5),
Price(),
SizedBox(width:5),
New(),
],
),
);
}
}
class Gcolor {
static Color background1 = Colors.white;
static Color font1 = Colors.blue;
static Color background2 = Colors.white;
static Color font2 = Colors.blue;
static Color background3 = Colors.white;
static Color font3 = Colors.blue;
static int times2=0;
static int times3=0;
static var pressed =true;
}
class Dis extends StatefulWidget {
const Dis({Key? key,}) : super(key: key);
#override
State<Dis> createState() => _DisState();
}
class _DisState extends State<Dis> {
var clicked=false;
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background1),
foregroundColor: MaterialStateProperty.all(Gcolor.font1),
),
onPressed: (){
setState((){
clicked=!clicked;
if(clicked){
Gcolor.background1=Colors.blue;
Gcolor.font1=Colors.white;
}
else{
Gcolor.background1=Colors.white;
Gcolor.font1=Colors.blue;
}
});
},
child: Row(
children: [
Text("距離 "),
Icon(Icons.add_location_outlined,
size: 20,),
],
)
),
);
}
}
class Price extends StatefulWidget {
const Price({Key? key}) : super(key: key);
#override
State<Price> createState() => _PriceState();
}
class _PriceState extends State<Price> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background2),
foregroundColor: MaterialStateProperty.all(Gcolor.font2),
),
onPressed: (){
setState((){
if(Gcolor.pressed){
Gcolor.times2++;
if(Gcolor.times2%3==1){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==2){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==0){
Gcolor.background2=Colors.white;
Gcolor.font2=Colors.blue;
}
}
});
},
child: Row(
children: [
Text("價格"),
Icon(Icons.attach_money_outlined,
size: 15,),
if(Gcolor.times2%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times2%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class New extends StatefulWidget {
const New({Key? key}) : super(key: key);
#override
State<New> createState() => _NewState();
}
class _NewState extends State<New> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background3),
foregroundColor: MaterialStateProperty.all(Gcolor.font3),
),
onPressed: (){
setState((){
Gcolor.times3++;
if(Gcolor.times3%3==1){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==2){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==0){
Gcolor.background3=Colors.white;
Gcolor.font3=Colors.blue;
}
});
},
child: Row(
children: [
Text("最新"),
Icon(Icons.access_time,
size:15 ,),
if(Gcolor.times3%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times3%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
I have updated your code to manage it to radio button. For price and new classes, I don't find any proper selection there. So just put it in initstate and you can change color based on selection.
Button classes:
class Dis extends StatefulWidget {
Dis({required this.isSelected, required this.onClicked, Key? key,}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<Dis> createState() => _DisState();
}
class _DisState extends State<Dis> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(widget.isSelected ? Colors.blue : Colors.white),
foregroundColor: MaterialStateProperty.all(widget.isSelected ? Colors.white : Colors.blue),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("距離 "),
Icon(Icons.add_location_outlined,
size: 20,),
],
)
),
);
}
}
class Price extends StatefulWidget {
Price({required this.isSelected, required this.onClicked,Key? key}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<Price> createState() => _PriceState();
}
class _PriceState extends State<Price> {
#override
void initState() {
super.initState();
if(widget.isSelected){
Gcolor.times2++;
if(Gcolor.times2%3==1){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==2){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==0){
Gcolor.background2=Colors.white;
Gcolor.font2=Colors.blue;
}
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background2),
foregroundColor: MaterialStateProperty.all(Gcolor.font2),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("價格"),
Icon(Icons.attach_money_outlined,
size: 15,),
if(Gcolor.times2%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times2%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class New extends StatefulWidget {
New({required this.isSelected, required this.onClicked,Key? key}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<New> createState() => _NewState();
}
class _NewState extends State<New> {
#override
void initState() {
super.initState();
Gcolor.times3++;
if(Gcolor.times3%3==1){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==2){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==0){
Gcolor.background3=Colors.white;
Gcolor.font3=Colors.blue;
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background3),
foregroundColor: MaterialStateProperty.all(Gcolor.font3),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("最新"),
Icon(Icons.access_time,
size:15 ,),
if(Gcolor.times3%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times3%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class Gcolor {
static Color background1 = Colors.white;
static Color font1 = Colors.blue;
static Color background2 = Colors.white;
static Color font2 = Colors.blue;
static Color background3 = Colors.white;
static Color font3 = Colors.blue;
static int times2=0;
static int times3=0;
static var pressed =true;
}
Row:
Row(
children: [
Dis(isSelected: _selectedIndex == 0, onClicked: (){
setState((){
_selectedIndex = 0;
});
}),
SizedBox(width:5),
Price(isSelected: _selectedIndex == 1, onClicked: (){
setState((){
_selectedIndex = 1;
});
}),
SizedBox(width:5),
New(isSelected: _selectedIndex == 2, onClicked: (){
setState((){
_selectedIndex = 2;
});
}),
],
)
Please let me know if this don't work for you.
I am trying to run method doAnimation from another widget by clicking on a FloatingActionButton.
Please tell me how to do this with this simple example. I know how to do this using the Provider package, but the code is cumbersome. How can I do this using Flutter's native methods?
Or, most likely, it can be done nicely with the Provider, but I don't know how.
A similar question has already been asked, but the second version of flutter and dart has already been released.
State management difficulties are probably the biggest newbies problem.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyAnimation(),
floatingActionButton: FloatingActionButton(
onPressed: () {
//TODO! error is here
_MyAnimationState().doAnimation();
//MyAnimation().createState().doAnimation(); // ?
},
child: Icon(Icons.play_arrow),
),
);
}
}
class MyAnimation extends StatefulWidget {
const MyAnimation({Key? key}) : super(key: key);
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height = 250;
bool _isOpen = true;
void doAnimation() {
_isOpen = !_isOpen;
setState(() {
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
doAnimation(); // works as it should
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
This error occurs when I try to use the class methods in the usual way.
This happens when you call setState() on a State object for a widget that hasn't been inserted into the widget tree yet. It is not necessary to call setState() in the constructor, since the state is already assumed to be dirty when it is initially created.
This is actually quite simple and doesn't require any kind of package. You can do this with the help of global keys. First create a global key like this GlobalKey<_MyAnimationState> _key = GlobalKey<_MyAnimationState>();. Then pass this key while using MyAnimation class like this MyAnimation(key: _key). Now use this key in the onPressed function to call the doAnimation method like this _key.currentState!.doAnimation();
Here is the complete implementation.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
GlobalKey<_MyAnimationState> _key = GlobalKey<_MyAnimationState>(); // declaration of the key
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyAnimation(key: _key), // passing the key
floatingActionButton: FloatingActionButton(
onPressed: () {
_key.currentState!.doAnimation(); // calling the method from child widget
},
child: Icon(Icons.play_arrow),
),
);
}
}
class MyAnimation extends StatefulWidget {
const MyAnimation({Key? key}) : super(key: key);
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height = 250;
bool _isOpen = true;
void doAnimation() {
_isOpen = !_isOpen;
setState(() {
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
doAnimation(); // works as it should
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
You can try to write the "doAnimation()" in the initState() instead the use of the floatingActionButton in order to trigger this action when the widget be initialized.
double _height;
bool _isOpen;
#override
void initState() {
super.initState();
bool _isOpen = true;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
}
Then use the ElevatedButton as the setter of the bool and setState to re-rendered the widgets:
ElevatedButton(
onPressed: () {
setState(() {
_isOpen = !_isOpen;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
Your code will look like this:
class MyAnimation extends StatefulWidget {
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height;
bool _isOpen;
#override
void initState() {
super.initState();
bool _isOpen = true;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
setState(() {
_isOpen = !_isOpen;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
You can also use the Provider to set the _isOpen value with setter function and avoid all these validations on the widget buttons.
What I want is when I click the switch button, the text in the Option1Content widget should change to true or false (depending upon the current value of the switch). The value is correct when you click the tile, select a different option from the drawer, and then come back to option 1, you will have the correct value. My requirement is when I press the switch tile the value of Option1Content should change instantly. For functionality reference: https://dartpad.dev/c9cabc35a0bda57758b1d1cf07f8a823. Any help would be greatly appreciated. Thank you.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget{
MyWidgetState createState()=> MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
bool status;
Widget myBody;
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void closeDrawer() {
if (_scaffoldKey.currentState.isDrawerOpen) {
_scaffoldKey.currentState.openEndDrawer();
}
}
#override
void initState(){
super.initState();
status = false;
myBody = Option1Content(status:status);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key:_scaffoldKey,
appBar:AppBar(
iconTheme: IconThemeData(color: Colors.black),
elevation:0,
backgroundColor:Colors.transparent,
actions:[
Switch(
inactiveThumbColor: Colors.black,
activeColor: Colors.green,
value:status,
onChanged:(value){
setState((){
status=value;
});
})
]
),
drawer: Drawer(
child:Center(child:ListView(children:[
DrawerHeader(
child: Column(
children: <Widget>[
CircleAvatar(
radius: 50,
backgroundColor: Colors.grey,
),
Padding(
padding: const EdgeInsets.only(
left: 8.0, right: 8.0, top: 12.0),
child: Text(
'Account',
style: TextStyle(
fontWeight: FontWeight.bold,
),
textScaleFactor: 1.3,
),
),
],
),
),
ListTile(title:Center(child:Text('Option 1')),onTap:(){
closeDrawer();
setState((){
myBody = Option1Content(status:status);
});
}),
ListTile(title:Center(child:Text('Option 2')),onTap:(){
closeDrawer();
setState((){
myBody = Center(child:Text('Option 2 Content'));
});
}),
ListTile(title:Center(child:Text('Option 3')),onTap:(){
closeDrawer();
setState((){
myBody = Center(child:Text('Option 3 Content'));
});
}),
]))
),
body: myBody
);
}
}
class Option1Content extends StatefulWidget {
final bool status;
Option1Content({#required this.status});
#override
_Option1ContentState createState() => _Option1ContentState();
}
class _Option1ContentState extends State<Option1Content> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('${widget.status}'),
);
}
}
The issue is that simply changing the value of status doesn't update what is actually in myBody, which is what's shown. Even when changing status with setState, myBody still contains your widget with the old value of status. This is why when you go to another myBody and come back, it's updated, because myBody now has the new widget with the updated status value.
To solve this you need to have a method of updating what's contained in myBody, because that's the only part that's being built. Doing the following is the simplest change.
Just change
setState((){
status = value;
});
to
setState((){
status = value;
myBody = Option1Content(status:status);
});
and the full code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget{
MyWidgetState createState()=> MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
bool status;
Widget myBody;
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void closeDrawer() {
if (_scaffoldKey.currentState.isDrawerOpen) {
_scaffoldKey.currentState.openEndDrawer();
}
}
#override
void initState(){
super.initState();
status = false;
myBody = Option1Content(status:status);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key:_scaffoldKey,
appBar:AppBar(
iconTheme: IconThemeData(color: Colors.black),
elevation:0,
backgroundColor:Colors.transparent,
actions:[
Switch(
inactiveThumbColor: Colors.black,
activeColor: Colors.green,
value:status,
onChanged:(value){
setState((){
status = value;
myBody = Option1Content(status:status);
});
})
]
),
drawer: Drawer(
child:Center(child:ListView(children:[
DrawerHeader(
child: Column(
children: <Widget>[
CircleAvatar(
radius: 50,
backgroundColor: Colors.grey,
),
Padding(
padding: const EdgeInsets.only(
left: 8.0, right: 8.0, top: 12.0),
child: Text(
'Account',
style: TextStyle(
fontWeight: FontWeight.bold,
),
textScaleFactor: 1.3,
),
),
],
),
),
ListTile(title:Center(child:Text('Option 1')),onTap:(){
closeDrawer();
setState((){
myBody = Option1Content(status:status);
});
}),
ListTile(title:Center(child:Text('Option 2')),onTap:(){
closeDrawer();
setState((){
myBody = Center(child:Text('Option 2 Content'));
});
}),
ListTile(title:Center(child:Text('Option 3')),onTap:(){
closeDrawer();
setState((){
myBody = Center(child:Text('Option 3 Content'));
});
}),
]))
),
body: myBody
);
}
}
class Option1Content extends StatefulWidget {
final bool status;
Option1Content({#required this.status});
#override
_Option1ContentState createState() => _Option1ContentState();
}
class _Option1ContentState extends State<Option1Content> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('${widget.status}'),
);
}
}
I have a two-page app. On-Page One I am showing an UUID which changes every 1 second. It is shown using listview. Once the user clicks on the list view it goes to the second page and shows the data on that card.
It should have been the changing UUID. but the data shown is static UUID. How I can pass the data changed on page 1 to page 2?
import 'dart:async';
import 'package:uuid/uuid.dart';
import 'package:uuid/uuid_util.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
List<EuropeanCountries> europeanCountries = [];
class EuropeanCountries {
String myText;
String myUuid;
EuropeanCountries({
this.myText,
this.myUuid,
});
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
int _perPage = 50;
ScrollController _myScrollController = ScrollController();
void _incrementCounter() async {
const ThreeSec = const Duration(seconds: 1);
this._counter++;
europeanCountries.insert(
0,
EuropeanCountries(
myText: this._counter.toString(),
));
print(europeanCountries[0].myText);
setState(() {});
}
void getMoreData() {
print('adding More Product ');
europeanCountries.add(EuropeanCountries(
myText: this._counter.toString(),
));
//europeanCountries.insert(0, EuropeanCountries(myText:this._counter.toString(), myButtonText: "", myColor: Colors.blue));
setState(() {});
}
void generateUUID() async {
var uuid = Uuid();
for (int i = 0; i < 6000; i++) {
await new Future.delayed(new Duration(milliseconds: 1000));
for (EuropeanCountries currCountry in europeanCountries) {
currCountry.myUuid = uuid.v1();
}
setState(() {});
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
generateUUID();
_myScrollController.addListener(() {
double maxscroll = _myScrollController.position.maxScrollExtent;
double currentScroll = _myScrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.25;
print("mac Scroll Controller - " + maxscroll.toString());
print("Current Scroll Controller - " + currentScroll.toString());
print("delta Scroll Controller - " + delta.toString());
if ((maxscroll - currentScroll) < delta) {
getMoreData();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _myListView(context),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _myListView(BuildContext context) {
// backing data
return Container(
child: europeanCountries.length == 0
? Center(
child: Text('No Product to Display'),
)
: ListView.builder(
controller: _myScrollController,
itemCount: europeanCountries.length,
reverse: false,
itemBuilder: (context, index) {
return myContainer(index: index);
},
),
);
}
}
class myContainer extends StatefulWidget {
final int index;
const myContainer({Key key, this.index}) : super(key: key);
#override
_myContainerState createState() => _myContainerState();
}
class _myContainerState extends State<myContainer> {
#override
Widget build(BuildContext context) {
return Container(
height: 120,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue[700]),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
margin: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text(europeanCountries[widget.index].myText),
SizedBox(
height: 15,
),
RaisedButton(
child: Text('Detail'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondRoute(
myCountry: europeanCountries[widget.index],
)),
);
},
color: Colors.blue[700],
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.black,
),
Text(europeanCountries[widget.index].myUuid != null
? europeanCountries[widget.index].myUuid
: 'Default')
],
),
);
}
}
class SecondRoute extends StatefulWidget {
final EuropeanCountries myCountry;
const SecondRoute({Key key, this.myCountry}) : super(key: key);
#override
_SecondRouteState createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
#override
void didUpdateWidget(SecondRoute oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Text(widget.myCountry.myUuid != null
? widget.myCountry.myText
: 'default'),
),
SizedBox(height: 15),
Center(
child: Text(widget.myCountry.myUuid != null
? widget.myCountry.myUuid
: 'default'),
),
],
),
),
),
);
}
}
https://imgur.com/VqRfcZY
<blockquote class="imgur-embed-pub" lang="en" data-id="VqRfcZY"></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
In the following Flutter app, I'm trying to show a LinearProgressIndicator in each card only when that card is counting. The the correct progression is printed to the console, but I can't figure out how to access "stepProgress" variable from the LinearProgressIndicator widget to update the view.
The cards are being built with a builder because they will change based on the input List (Array) of Maps (Objects).
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
final key = new GlobalKey<_MyHomePageState>();
List<Widget> cards = [];
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App Title',
theme: ThemeData(
primarySwatch: Colors.blue,
canvasColor: Colors.grey[350],
),
home: MyHomePage(title: 'Title', key: key),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _sequence = [];
double stepProgress = 0.0;
#override
initState() {
super.initState();
setState(() => _sequence = [
{'iterations': 1, 'time': 10},
{'iterations': 3, 'time': 7},
{'iterations': 2, 'time': 5},
]);
setState(() {
cards = getRun();
});
_countdown(_sequence, null);
}
getRun() {
List<Widget> runCards = [];
for (var group in _sequence) {
runCards.add(_buildCard(CardModel(
iterationsInGroup: group['iterations'],
timeEach: group['time'],
)));
}
return runCards;
}
void _countdown(seq, iters) async {
if (seq.length > 0) {
int i = iters == null ? seq[0]['iterations'] : iters;
if (i > 0) {
int duration = seq[0]["time"];
Timer.periodic(Duration(seconds: 1), (timer) {
if (timer.tick < duration) {
setState(() {
stepProgress = timer.tick / duration;
});
print('Iteration $i: ${timer.tick} / $duration = $stepProgress');
} else {
print('Finished iteration $i');
timer.cancel();
i = i - 1;
if (i > 0) {
_countdown(seq, i); // Next iteration
} else {
print('Finished group ${seq.length}');
timer.cancel();
if (seq.length > 1) {
_countdown(seq.sublist(1), null); // Next group
} else {
print('Done');
}
}
}
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
'Header',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: ListView(
children: cards,
padding: const EdgeInsets.all(8.0),
),
),
],
),
);
}
}
Widget _buildCard(CardModel card) {
List<Widget> columnData = <Widget>[];
columnData.add(
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
card.timeEach.toString() +
' seconds ' +
card.iterationsInGroup.toString() +
' times',
style: TextStyle(fontSize: 22.0),
),
),
true //key.currentState.activeStep == card.cardStep //TODO: This doesn't work
? LinearProgressIndicator(
value: key.currentState.stepProgress,
)
: Container(width: 0.0, height: 0.0),
],
),
);
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: columnData),
),
);
}
class CardModel {
final int iterationsInGroup;
final int timeEach;
CardModel({
this.iterationsInGroup,
this.timeEach,
});
}
I modified your code a little, this is a little messy therefore I recommend you the following:
Create a StatefulWidget for your ChildView (Like my code below).
Keep the progress logic into the ChildView, it would be easy to change the status in that way.
I had to keep track the Globalkey in order to refresh the changes into the child view, but if you handle the logic into each child, you don't need the GlobalKey.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
final key = new GlobalKey<_MyHomePageState>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App Title',
theme: ThemeData(
primarySwatch: Colors.blue,
canvasColor: Colors.grey[350],
),
home: MyHomePage(title: 'Title', key: key),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _sequence = [];
List<ChildView> runCards = [];
#override
initState() {
super.initState();
setState(() => _sequence = [
{'iterations': 1, 'time': 10, 'progress': 0.0},
{'iterations': 3, 'time': 7, 'progress': 0.0},
{'iterations': 2, 'time': 5, 'progress': 0.0},
]);
getRun();
_countdown(_sequence, null);
}
getRun() {
for (var group in _sequence) {
var cardModel = CardModel(
iterationsInGroup: group['iterations'],
timeEach: group['time'],
progress: group['progress'],
);
runCards.add(new ChildView(cardModel,new GlobalKey<_ChildViewState>()));
}
setState(() {
});
return runCards;
}
void _countdown(seq, iters) async {
if (seq.length > 0) {
int i = iters == null ? seq[0]['iterations'] : iters;
if (i > 0) {
int duration = seq[0]["time"];
Timer.periodic(Duration(seconds: 1), (timer) {
if (timer.tick <= duration) {
var childView = runCards[i-1];
double stepProgress = 0.0;
stepProgress = timer.tick / duration;
childView.key.currentState.updateProgress(stepProgress);
print('Iteration $i: ${timer.tick} / $duration = $stepProgress');
} else {
print('Finished iteration $i');
timer.cancel();
i = i - 1;
if (i > 0) {
_countdown(seq, i); // Next iteration
} else {
print('Finished group ${seq.length}');
timer.cancel();
if (seq.length > 1) {
_countdown(seq.sublist(1), null); // Next group
} else {
print('Done');
}
}
}
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
'Header',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: ListView(
children: runCards,
padding: const EdgeInsets.all(8.0),
),
),
],
),
);
}
}
class CardModel {
final int iterationsInGroup;
final int timeEach;
double progress;
CardModel({
this.iterationsInGroup,
this.timeEach,
this.progress,
});
}
class ChildView extends StatefulWidget {
final CardModel card;
final GlobalKey<_ChildViewState> key;
ChildView(this.card, this.key) : super(key: key);
#override
_ChildViewState createState() => _ChildViewState();
}
class _ChildViewState extends State<ChildView> {
void updateProgress(double progress){
setState(() {
widget.card.progress = progress;
});
}
#override
Widget build(BuildContext context) {
List<Widget> columnData = <Widget>[];
columnData.add(
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.card.timeEach.toString() +
' seconds ' +
widget.card.iterationsInGroup.toString() +
' times',
style: TextStyle(fontSize: 22.0),
),
),
widget.card.progress < 1 //key.currentState.activeStep == card.cardStep //TODO: This doesn't work
? LinearProgressIndicator(
value: widget.card.progress,
)
: Container( child: new Text("Completed"),),
],
),
);
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: columnData),
),
);
}
}