Flutter: Calling SetState() from another class - flutter

I am trying to make a simple image that appears or disappears when a button is pushed. This button resides in a separate class to the image, so in Flutter this creates a massive headache of an issue.
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
What I am trying to do:
class SinglePlayerMode extends StatefulWidget {
#override
SinglePlayerModeParentState createState() => SinglePlayerModeParentState();
}
class SinglePlayerModeParentState extends State<SinglePlayerMode> {\
bool coinVisible = false;
toggleCoin() {
setState(() {
coinVisible = !coinVisible;
});
}
Widget topMenuRow() {
return Stack(
children: [
Column(
children: [
coinVisible == true ?
Padding(
padding: EdgeInsets.all(50),
child: Container(
height: 60,
width: 60,
color: Colors.blueGrey[0],
decoration: BoxDecoration(
color: Colors.blueAccent,
image: DecorationImage(
image: ExactAssetImage('lib/images/coin_head.jpg'),
fit: BoxFit.cover,
),
),
),
) : Container(
height: 60,
width: 60,
color: Colors.black,
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
children: [
topMenuRow(),
SizedBox(height: 40),
],
),
),
);
}
And this is the separate class which I would like to trigger the SetState() on coinVisible from:
class dropDownMenu extends StatefulWidget { #override
_dropDownMenuState createState() => _dropDownMenuState();
}
class _dropDownMenuState extends State<dropDownMenu> {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: (){
//SOMEHOW CALL SetState() ON coinVisble HERE!
},
),
),
);
}
}
But nothing I have tried is working, and I have lost hours.

It simple, you need to send your SinglePlayMode::toggleCoin function as callback to dropDownMenu class.
class dropDownMenu extends StatefulWidget {
final _callback; // callback reference holder
//you will pass the callback here in constructor
dropDownMenu( {#required void toggleCoinCallback() } ) :
_callback = toggleCoinCallback;
#override
_dropDownMenuState createState() => _dropDownMenuState();
}
class _dropDownMenuState extends State<dropDownMenu> {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: (){
widget?._callback(); // callback calling
},
),
),
);
}
}
Then when you create a dropDownMenu class instance in your SinglePlayerMode class you will do
dropDownMenu(
toggleCoinCallback: toogleCoin,
);

Related

Flutter - Row added -> change the text of a container

I'm quite inexperienced with flutter and have created this script.
When you tap on the red container you create a Row of buttons,
I would like when I click on a button in the Row -> the text of the blue container becomes the same as the text contained in the tapped button
Anyone know how I can do?
Thank you :)
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele!';
List<Container> OutputList = [];
void tool(String text) async {
List ListText = text.split(' ');
for (var i in ListText) {
OutputList.add(
Container(
child: GestureDetector(
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
color: Colors.orange,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(i),
),
),
),
),
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
tool(text);
print(OutputList);
});
},
child: Container(
width: 150.0,
height: 50.0,
color: Colors.red,
child: Center(child: Text('START ->')),
),
),
SizedBox(height: 50.0),
Row(
children: OutputList,
),
SizedBox(height: 50.0),
Container(
color: Colors.blue,
width: 200.0,
height: 50.0,
child: Text(''),
),
],
),
),
);
}
}
Yes you can add a few line of code check here i try to solve.
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele!';
//step 1 create variable
String newGeneratedText = "";
List<Container> OutputList = [];
void tool(String text) async {
List ListText = text.split(' ');
for (var i in ListText) {
OutputList.add(
Container(
child: GestureDetector(
onTap: () {
//add logic here to concatinate values
setState(() {
newGeneratedText = newGeneratedText + " " + i;//added " " for one space
});
},
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
color: Colors.orange,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(i),
),
),
),
),
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
tool(text);
print(OutputList);
});
},
child: Container(
width: 150.0,
height: 50.0,
color: Colors.red,
child: Center(child: Text('START ->')),
),
),
SizedBox(height: 50.0),
Wrap( // added for fixing more values and solve overflow exceptions error
children: OutputList,
),
SizedBox(height: 50.0),
Container(
color: Colors.blue,
width: 200.0,
height: 50.0,
child: Text(newGeneratedText), //final print values
),
],
),
),
);
}
}

setState not updating

I just can't figure out what is the problem with this set state method in flutter. Everything seems okay. But the text is not updating on onPressed.
class NetBalanceWidget extends StatefulWidget {
#override
_NetBalanceWidgetState createState() => _NetBalanceWidgetState();
}
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
#override
Widget build(BuildContext context) {
String text = 'NetBalance-Amount';
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}
You have text as a local variable in the build method. setState is essentially just calling build again, and is resetting the value of text back to its default of 'NetBalance-Amount'.
Move its declaration outside of build:
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
String text = 'NetBalance-Amount';
#override
Widget build(BuildContext context) {
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}

Listview scrolling and selecting Textfield afterwards is freezing my app

I am using the package
country_code_picker: ^1.4.0
https://pub.dev/packages/country_code_picker#-installing-tab-
with flutter 1.17.3
Which is pretty much one of the only country code picker packages. But I have one serious problem an I don't have a clue what it could be.
When I run this code
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
App();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TestWidget(),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildCountryPicker(context));
}
Widget _buildCountryPicker(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: CountryCodePicker(
initialSelection: 'NL',
),
),
);
}
}
And I open the dialog to select a country. I scroll in the list and then select the TextField my keyboard opens and when I try to type something my entire app freezes. I can't even hot reload. I don't get a single error.
I am running this on my Huawei P30, but I also experience this on other android devices. I don't know if this is a flutter bug or a country code picker bug.
I think it is probably in this widget somewhere. If anyone could point me in the right direction it would help me alot!
class SelectionDialog extends StatefulWidget {
final List<CountryCode> elements;
final bool showCountryOnly;
final InputDecoration searchDecoration;
final TextStyle searchStyle;
final TextStyle textStyle;
final WidgetBuilder emptySearchBuilder;
final bool showFlag;
final double flagWidth;
final Size size;
final bool hideSearch;
/// elements passed as favorite
final List<CountryCode> favoriteElements;
SelectionDialog(
this.elements,
this.favoriteElements, {
Key key,
this.showCountryOnly,
this.emptySearchBuilder,
InputDecoration searchDecoration = const InputDecoration(),
this.searchStyle,
this.textStyle,
this.showFlag,
this.flagWidth = 32,
this.size,
this.hideSearch = false,
}) : assert(searchDecoration != null, 'searchDecoration must not be null!'),
this.searchDecoration =
searchDecoration.copyWith(prefixIcon: Icon(Icons.search)),
super(key: key);
#override
State<StatefulWidget> createState() => _SelectionDialogState();
}
class _SelectionDialogState extends State<SelectionDialog> {
/// this is useful for filtering purpose
List<CountryCode> filteredElements;
#override
Widget build(BuildContext context) => SimpleDialog(
titlePadding: const EdgeInsets.all(0),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
IconButton(
padding: const EdgeInsets.all(0),
iconSize: 20,
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
if (!widget.hideSearch)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextField(
style: widget.searchStyle,
decoration: widget.searchDecoration,
onChanged: _filterElements,
),
),
],
),
children: [
Container(
width: widget.size?.width ?? MediaQuery.of(context).size.width,
height:
widget.size?.height ?? MediaQuery.of(context).size.height * 0.7,
child: ListView(
children: [
widget.favoriteElements.isEmpty
? const DecoratedBox(decoration: BoxDecoration())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.favoriteElements.map(
(f) => SimpleDialogOption(
child: _buildOption(f),
onPressed: () {
_selectItem(f);
},
),
),
const Divider(),
],
),
if (filteredElements.isEmpty)
_buildEmptySearchWidget(context)
else
...filteredElements.map(
(e) => SimpleDialogOption(
key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),
],
),
),
],
);
Widget _buildOption(CountryCode e) {
return Container(
width: 400,
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
if (widget.showFlag)
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Image.asset(
e.flagUri,
package: 'country_code_picker',
width: widget.flagWidth,
),
),
),
Expanded(
flex: 4,
child: Text(
widget.showCountryOnly
? e.toCountryStringOnly()
: e.toLongString(),
overflow: TextOverflow.fade,
style: widget.textStyle,
),
),
],
),
);
}
Widget _buildEmptySearchWidget(BuildContext context) {
if (widget.emptySearchBuilder != null) {
return widget.emptySearchBuilder(context);
}
return Center(
child: Text('No country found'),
);
}
#override
void initState() {
filteredElements = widget.elements;
super.initState();
}
void _filterElements(String s) {
s = s.toUpperCase();
setState(() {
filteredElements = widget.elements
.where((e) =>
e.code.contains(s) ||
e.dialCode.contains(s) ||
e.name.toUpperCase().contains(s))
.toList();
});
}
void _selectItem(CountryCode e) {
Navigator.pop(context, e);
}
}
Also filed an issue on the flutter github https://github.com/flutter/flutter/issues/59886
Edit:
I have a video of it right here
https://www.youtube.com/watch?v=669KitFG9ek&feature=youtu.be
I just had to remove the keys, so there probably was a duplicate key
...filteredElements.map(
(e) => SimpleDialogOption(
//key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),

Calling setState() from another page - Flutter

I have a String _value, which I would like to call it from another page.
Here is my players.dart
class MyWidgetPopup extends StatefulWidget {
#override
MyWidgetPopupState createState() => MyWidgetPopupState();
}
enum Answers{FIRST,SECOND}
class MyWidgetPopupState extends State<MyWidgetPopup> {
String _value = '';
void _setValue(String value) => setState(() => _value = value);
Future selectGk(BuildContext context) async {
switch(
await showDialog(
...
...
{
case Answers.FIRST:
_setValue('FIRST TEAM');
break;
case Answers.SECOND:
_setValue('SECOND TEAM');
break;
}
}
So, in players.dart, I can show the selected team with simple code: Text(_value)
But I would like to show it in select.dart
import 'package:flutter/material.dart';
import 'package:myapp/players.dart';
MyWidgetPopupState dataSource = MyWidgetPopupState();
class Besdortbir extends StatefulWidget {
#override
_BesdortbirState createState() => _BesdortbirState();
}
class _BesdortbirState extends State<Besdortbir> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'My Title'),
centerTitle: true,
backgroundColor: Colors.redAccent[700],
elevation: 0.0,
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(
image: new DecorationImage(
image: new AssetImage("assets/field.png"),
fit: BoxFit.fill
)
),
),
//FIRST PLAYER
Container(
padding: EdgeInsets.only(bottom: 90.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
GestureDetector(
onTap: (){dataSource.selectGk(context);}, //I have the FIRST TEAM and SECOND TEAM listed here.
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Image.asset('assets/PLUS.png',
scale: 8),
),
),
),
Text(//somehow I would like to show which team is selected by user),
],
),
),
],
),
);
}
}
I have tried to recall setState(), _value, and a few other options. But, I could not be successful.

How can I Initialize provider?

I have implemented Provider for state management in my app. Now, I need to add some data in the class once the screen is loaded.
How I can achieve this?
stepInfo.addToList = new VaccStep(); // Need to call it one time once screen is loaded.
I have tried to call this method from initState but it's giving error!!
class AdminAddVaccination extends StatefulWidget {
#override
State createState() => new AdminAddVaccinationState();
}
class AdminAddVaccinationState extends State<AdminAddVaccination> {
#override
void initState() {
super.initState();
var stepInfo = Provider.of<StepInfo>(context); // ERROR!!
stepInfo.addToList = new VaccStep(); // ERROR!!
}
Widget build(BuildContext context) {
return new ChangeNotifierProvider(
builder: (context) => StepInfo(),
child: ScreenBody(),
);
}
}
class ScreenBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
var stepInfo = Provider.of<StepInfo>(context);
return new Scaffold(
resizeToAvoidBottomPadding: false,
key: stepInfo.scaffoldKey,
body: new GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: new SafeArea(
top: true,
bottom: false,
child: new Stack(children: <Widget>[
new Opacity(
opacity: 0.04,
child: new Image.asset(
"assets/userProfile/heartBeat.png",
fit: BoxFit.cover,
height: 250.0,
),
),
new Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
color: primaryGreen,
width: double.infinity,
height: 65.0,
child: new Stack(
children: <Widget>[
new Align(
alignment: Alignment.center,
child: stepInfo.loading
? JumpingText('......')
: new Container()),
new Align(
alignment: Alignment.centerLeft,
child: new Padding(
padding: EdgeInsets.only(top: 5.0, left: 20.0),
child: new InkWell(
child: new Container(
child: Icon(
Icons.arrow_back,
color: Colors.white,
size: 30.0,
),
),
onTap: () {
Navigator.pop(context);
},
),
),
),
],
),
),
new Padding(
padding: EdgeInsets.only(top: 0.0),
child: new Material(
elevation: 1.0,
color: Colors.transparent,
child: new Container(
color: borderColor,
width: double.infinity,
height: 5.0,
),
),
),
VaccName(),
],
),
ItemListing(),
AddStep(),
]),
)));
}
}
Error!! flutter: The following ProviderNotFoundError was thrown
building Builder: flutter: Error: Could not find the correct
Provider above this AdminAddVaccination Widget flutter:
flutter: To fix, please: flutter: flutter: * Ensure the
Provider is an ancestor to this AdminAddVaccination Widget
flutter: * Provide types to Provider flutter: * Provide
types to Consumer flutter: * Provide types to
Provider.of() flutter: * Always use package imports. Ex:
`import 'package:my_app/my_code.dart';
Simply add a constructor in your provider :
class StepInfo extends ChangeNotifier {
StepInfo() {
this.addToList = new VaccStep();
}
[...]
}
You must set listen:false and some delay to on initstate
Provider.of<StepInfo>(context, listen: false);
Future.delayed(Duration(milliseconds: 100)).then((_) {
stepInfo.addToList = new VaccStep();
});
same in this case or this
change the initState() & build() method in the AdminAddVaccination class as below:
var stepInfo;
#override
void initState() {
super.initState();
stepInfo = new StepInfo();
stepInfo.addToList = new VaccStep();
}
#override
Widget build(BuildContext context) {
return new ChangeNotifierProvider<StepInfo>(
builder: (context) => stepInfo,
child: ScreenBody(),
);
}