Call a setState of a statefull widget from the stateless widget - flutter

I have a stateless widget class that has a widget whose movements need to be tracked. I cannot keep this widget inside the stateful widgets as I don't want the state of this widget to be refreshed.
I have the following code.
import 'package:flutter/material.dart';
import 'package:control_pad/control_pad.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: <Widget>[
Expanded(
child: JoystickView(
onDirectionChanged: (degree, direction) {
//Change the state here.
},
),
),
Expanded(
child: MyStateFull(),
),
],
),
);
}
}
class MyStateFull extends StatefulWidget {
#override
_MyStateFullState createState() => _MyStateFullState();
}
class _MyStateFullState extends State<MyStateFull> {
double degree = 10;
double direction = 10;
//Call this from the stateless Widget
void changedDirection(degree, direction) {
setState(() {
this.degree = degree;
this.direction = direction;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Text(
"The degree Moved is $degree and the direction is $direction",
style: TextStyle(fontSize: 25, color: Colors.black),
),
);
}
}
This code produces the following output.
I want the direction and degree values to be changed as the joystick is moved.
Thank You.

I tried it myself and found the solution. This can be done using streams. I will post the code just in case someone needs it in the future.
import 'package:flutter/material.dart';
import 'package:control_pad/control_pad.dart';
class MyStateLess extends StatelessWidget {
StreamController<List<double>> _controller = StreamController<List<double>>();
GlobalKey<_MyStateFullState> statefulKey = new GlobalKey<_MyStateFullState>();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
JoystickView(
onDirectionChanged: (degree, direction) {
List<double> temp = new List<double>();
temp.add(degree);
temp.add(direction);
_controller.add(temp);
},
),
MyStateFull(stream: _controller.stream, key: statefulKey),
],
);
}
}
class MyStateFull extends StatefulWidget {
final Stream<List<double>> stream;
MyStateFull({Key key, #required this.stream}) : super(key: key);
#override
_MyStateFullState createState() => _MyStateFullState();
}
class _MyStateFullState extends State<MyStateFull> {
double _degree = 0.0;
double _direction = 0.0;
#override
void initState() {
super.initState();
widget.stream.listen((event) {
setState(() {
_degree = event[0];
_direction = event[1];
});
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Text("$_degree, $_direction"),
);
}
}

Related

While learning flutter using (https://github.com/afitz0/exploration_planner). How to implement the action on the LinearProgressIndicator()?

This code is part of online training of flutter by Google team. The original code can be accessed in https://github.com/afitz0/exploration_planner. I am new on flutter and I´ve got some dificulties to use statefull widget. I still do not have enough confidence. I made some modification on original code to add action to the indicator bar, it works fine but I dont think my solution is ideal...
My question is related to the right way to make a change in the state of the taskitem give an
update on the linearProgressIndicator ? Thanks in advance..
import 'package:flutter/material.dart';
double _percentual = 0; //variable to hold progress bar values from zero to 1 step 0.2
// first comes root run appp
void main() => runApp(MyApp()
//MaterialApp
//Scaffold
//AppBar
//Text
//body: Column
//text, text, text
//image
//Row
//text, text, bttom
//....
);
// second comes materialapp
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Exploration!',
theme: ThemeData(primarySwatch: Colors.blueGrey),
home: MyHomePage(),
);
}
}
//third comes home page describes visual of app
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late AnimationController controller;
#override
void initState() {
controller = AnimationController(
vsync: this,
)..addListener(() {
setState(() {
controller.value = _percentual;
});
});
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Space Exploration planner'),
),
body: Column(
children: [
Progress(),
TaskList(),
],
),
);
}
}
class Progress extends StatefulWidget {
const Progress({super.key});
#override
State<Progress> createState() => _ProgressState();
}
class _ProgressState extends State<Progress> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text('You are this far away from exploring the whole universe'),
LinearProgressIndicator(
value: _percentual,
)
],
);
}
}
class TaskList extends StatelessWidget {
const TaskList({super.key});
#override
Widget build(BuildContext context) {
return Column(
children: [
TaskItem(label: "Load rocket with supplies"),
TaskItem(label: "Launch rocket"),
TaskItem(label: "Circle the home planet"),
TaskItem(label: "Head out to de first moon"),
TaskItem(label: "Launch moon lander #1"),
],
);
}
}
class TaskItem extends StatefulWidget {
final String label;
const TaskItem({Key? key, required this.label}) : super(key: key);
#override
State<TaskItem> createState() => _TaskItemState();
}
class _TaskItemState extends State<TaskItem> {
bool? _value = false;
#override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(
onChanged: (newValue) => setState(() => {
_value = newValue,
if (_value == true)
{
_percentual = double.parse(
(_percentual + 0.2).toStringAsPrecision(1)),
_ProgressState(),
}
else if (_value == false)
{
_percentual = double.parse(
(_percentual - 0.2).toStringAsPrecision(1)),
_ProgressState(),
},
main(), *//<-- worked like hot-reload but I dont think is the right way to do it.*
}),
value: _value,
),
Text(widget.label),
],
);
}
}

How to set the state of a stateful widget from a child stateless widget

Okay, so just to warn you, I'm 15 and I'm a complete flutter noob. This is my first ever project, so excuse the probably dumb question, and please go easy on me.
I have this stateful widget (ride) where the body is one of the child stateless widgets defined in _children. The if statement just changes between the 1st and 2nd child widgets depending on if the user has connected a BT device (that part is irrelevant).
What I need to do is set the state from the inside of the MaterialButton found on ln 68 so that ride shows the riding stateless widget, but obviously I can't change the state from inside startRide because it's a stateless widget. How would I go about doing this?
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' ;
import 'results.dart';
import 'settings.dart';
class ride extends StatefulWidget {
#override
_rideState createState() => _rideState();
}
class _rideState extends State<ride> {
int _currentState = 0;
final List<Widget> _children = [
notConnected(),
startRide(),
riding(),
];
bool connected = checkBT(); // Function defined in settings.dart
#override
Widget build(BuildContext context) {
if (connected == true){
_currentState = 1;
setState((){_currentState;});
}
return _children[_currentState];
}
}
class notConnected extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height:180,
padding: EdgeInsets.fromLTRB(40, 0, 40, 0),
child: Center(
child: Text(
"Oops! Looks like your phone isn’t connected to your bluetooth device.",
style:Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
),
),
);
}
}
class startRide extends StatelessWidget {
AudioPlayer _audioPlayer = AudioPlayer();
AudioCache player = AudioCache();
#override
Widget build(BuildContext context) {
return Scaffold(
body:Center(
child: Container(
width: 200,
height: 80,
child: MaterialButton(
onPressed:(){
player.play("beeps.mp3");
// I NEED TO SET THE STATE OF RIDE HERE
},
child: Text(
"Start!",
style: Theme.of(context).textTheme.headline1,
),
color: Colors.red[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
),
),
),
);
}
}
class riding extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(); //not finished writing this yet
}
}
I'm probably going about doing this in completely the wrong way, but I've come from python so it's very different. Any help would be greatly appreciated :)
You can create callback, i.e passing function
Here is a sample code
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' ;
import 'results.dart';
import 'settings.dart';
class ride extends StatefulWidget {
#override
_rideState createState() => _rideState();
}
class _rideState extends State<ride> {
int _currentState = 0;
final List<Widget> _children = [
notConnected(),
startRide((){
// you can setState((){}) here
}),
riding(),
];
bool connected = checkBT(); // Function defined in settings.dart
#override
Widget build(BuildContext context) {
if (connected == true){
_currentState = 1;
setState((){_currentState;});
}
return _children[_currentState];
}
}
class notConnected extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height:180,
padding: EdgeInsets.fromLTRB(40, 0, 40, 0),
child: Center(
child: Text(
"Oops! Looks like your phone isn’t connected to your bluetooth device.",
style:Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
),
),
);
}
}
class startRide extends StatelessWidget {
AudioPlayer _audioPlayer = AudioPlayer();
AudioCache player = AudioCache();
Function callback;
startRide(Function callback){
this.callback = callback;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:Center(
child: Container(
width: 200,
height: 80,
child: MaterialButton(
onPressed:(){
player.play("beeps.mp3");
// I NEED TO SET THE STATE OF RIDE HERE
// callback function
callback();
},
child: Text(
"Start!",
style: Theme.of(context).textTheme.headline1,
),
color: Colors.red[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
),
),
),
);
}
}
class riding extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(); //not finished writing this yet
}
}
Edit :- Test code
import 'package:flutter/material.dart';
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> {
int current = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: root(),
);
}
Widget root() {
return Container(
child: TestingStateless((){
setState(() {
current++;
print(current);
});
}),
);
}
}
// ignore: must_be_immutable
class TestingStateless extends StatelessWidget{
Function func;
TestingStateless(Function func){
this.func = func;
}
#override
Widget build(BuildContext context) {
return InkWell(
onTap: (){
func();
},
child: Container(
height: 50,
color: Colors.green,
child: Text('TESTING'),
),
);
}
}

Focus first focusable element below a Focus widget

I am trying to focus the first focusable element below a Focus widget. Consider the following artifical example. It contains a BarWidget with some TextField.
The FooWidget wraps it into a Focus widget with the given focusNode. After one second, I'd like to focus the first element of BarWidget. Please not that I don't want to pass a FocusNode down into BarWidget.
import 'package:flutter/material.dart';
void main() {
runApp(Foo());
}
class Foo extends StatefulWidget {
#override
State createState() => FooState();
}
class FooState extends State<Foo> {
final focusNode = FocusNode();
void initState() {
super.initState();
Future.delayed(
const Duration(seconds: 1),
focusNode.requestFocus,
);
}
#override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: BarWidget(),
),
),
);
}
class BarWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => Column(
children: [
Text("Foo"),
TextField(),
Text("Bar"),
],
);
}
It is in theory possible by using FocusTraversalPolicy.sortDescendants. However, that method is declared as #protected. See: https://github.com/flutter/flutter/issues/70534
Full example:
import 'package:flutter/material.dart';
void main() {
runApp(Foo());
}
class Foo extends StatefulWidget {
#override
State createState() => FooState();
}
class FooState extends State<Foo> {
final focusNode = FocusNode(canRequestFocus: false);
void initState() {
super.initState();
void f() {
final policy = FocusTraversalGroup.of(focusNode.context);
// ignore: invalid_use_of_protected_member
final node = policy.sortDescendants(focusNode.traversalDescendants, null).first;
node.requestFocus();
}
Future.delayed(
const Duration(seconds: 1),
f,
);
}
#override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: BarWidget(),
),
),
);
}
class BarWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => Column(
children: [
Text("Foo"),
TextField(),
Text("Bar"),
],
);
}

There are some parts that I don't understand well about Flutter's Key

I practiced after watching a video explaining Flutter's Key.
https://api.flutter.dev/flutter/foundation/Key-class.html
This video shows an example of changing the location of a container with a specific color. (About 1 minute and 50 seconds)
In the video, the statefulwidget says that without a key, the location will not change.
But I wrote the example code myself and confirmed that it worked without giving a key to the stateful widget.
I think I wrote the example code wrong. Below is the code I wrote.
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',
home: KeyPractice(),
);
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({#required this.color});
final Color color;
#override
_StatefulColorfulTileState createState() => _StatefulColorfulTileState();
}
class _StatefulColorfulTileState extends State<StatefulColorfulTile> {
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: widget.color,
);
}
}
class KeyPractice extends StatefulWidget {
#override
_KeyPracticeState createState() => _KeyPracticeState();
}
class _KeyPracticeState extends State<KeyPractice> {
List<Widget> tiles;
#override
void initState() {
super.initState();
tiles = [
StatefulColorfulTile(
color: Colors.blueAccent,
),
StatefulColorfulTile(
color: Colors.amber,
),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: tiles,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.autorenew,
),
onPressed: () {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
},
),
);
}
}
The above codes switch positions with each other.
What happens to the example of how the widget does not reposition each other when the stateful widget in the video does not assign keys?
And I understand that the key works only on the Stateful widget, does the Stateless use the key?
And I understood that Key only works with the Stateful widget. I wonder if the Stateless widget uses a key.
If I misunderstood, please teach me.
You're storing the color in the State of KeyPractice. The example they use stores it in the State of the child, in your case: StatefulColorfulTile.
Below is an example of the use of keys to correctly reposition widgets like you're trying to do. My example ended up very similar to what's shown on this medium article. Removing the keys here prevents the widgets from reflecting the color swap, but the use of the keys allows for the intended behavior.
import 'package:flutter/material.dart';
import 'dart:math';
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',
home: KeyPractice(),
);
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({Key key}) : super(key: key);
#override
_StatefulColorfulTileState createState() => _StatefulColorfulTileState();
}
class _StatefulColorfulTileState extends State<StatefulColorfulTile> {
final Color myColor = UniqueColorGenerator.getColor();
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: myColor,
);
}
}
class KeyPractice extends StatefulWidget {
#override
_KeyPracticeState createState() => _KeyPracticeState();
}
class _KeyPracticeState extends State<KeyPractice> {
List<Widget> tiles;
#override
void initState() {
super.initState();
tiles = [
StatefulColorfulTile(key: UniqueKey()),
StatefulColorfulTile(key: UniqueKey()),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: tiles,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.autorenew,
),
onPressed: () {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
},
),
);
}
}
class UniqueColorGenerator {
static Random random = new Random();
static Color getColor() {
return Color.fromARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
}

Flutter setState() not refreshing widget when called from stateful child

I have two stateful widgets: ParentWidget and ChildWidget.
The ChildWidget has a gesture detector wrapping a container and text. When I call onTap the state of the Parent (status increased by 1) is updated properly but setState() is not refreshing the UI.
I tried everything: global keys, inherited widget but nothing works.
Interestingly if I change the ChildWidget to a stateless one then everything start working. Any ideas would be super helpful.
pk
Here is the code:
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int status = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Hexcolor('#1c486d'),
title: Text(
'Test',
),
),
body: ChildWidget(
child: GestureDetector(
onTap: () {
status = status + 1;
setState(() {}); // this is the problematic piece of code.
},
child: Container(
color: Colors.blue,
width: 100,
height: 100,
child: Text('PARENT:' + status.toString()),
),
),
),
);
}
}
class ChildWidget extends StatefulWidget {
final Widget child;
ChildWidget({this.child});
#override
_ChildWidgetState createState() => _ChildWidgetState(child);
}
class _ChildWidgetState extends State<ChildWidget> {
Widget child;
_ChildWidgetState(this.child);
#override
Widget build(BuildContext context) {
return child;
}
}
You can pass the parent's status to the ChildWidget so that when the parent's status changes, the ChildWidget's state changes and its build method be called.
body: GestureDetector(
onTap: () {
setState(() {
status = status + 1;
});
},
child: ChildWidget(status: status),
),
);
}
}
class ChildWidget extends StatefulWidget {
final int status;
ChildWidget({this.status});
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
_ChildWidgetState();
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
width: 100,
height: 100,
child: Text('PARENT:' + widget.status.toString()),
);
}
}
Try not making the child final
class ChildWidget extends StatefulWidget {
Widget child;
ChildWidget({this.child});
#override
_ChildWidgetState createState() => _ChildWidgetState(child);
}