Why does my custom flutter scaffold prevent the state from being refreshed? - flutter

Below is the minimal version of what i'm attempting to do with my app that maintains the issue. The slider still responds to being interacted with by showing it's animation, however it does not change the value or actually move. This only seems to occur because the Scaffold has been separated out to it's own widget.
I know that changing it to a stateless widget would fix it in this case, however the version in my app requires state changes.
I've tried adding UniqueKeys up and down the widget tree with no luck, though i'll admit I don't fully understand their use in this case.
Any advice, or a point in the right direction? Should I keep playing around with keys, or am I going about this in the wrong way?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scaffold Test',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _value = 20.0;
#override
Widget build(BuildContext context) {
return MyScaffold(
child: Center(
child: Slider(
min: 0,
max: 100,
value: _value,
onChanged: (double value) {
setState(() {
_value = value;
});
},
),
),
);
}
}
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => new MyScaffoldState(child);
}
class MyScaffoldState extends State<MyScaffold> {
Widget child;
MyScaffoldState(this.child);
#override
Widget build(BuildContext context) {
return Scaffold(
body: child,
);
}
}

As commented by user #pskink, removing the state constructor fixes the issue.
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => MyScaffoldState();
}
class MyScaffoldState extends State<MyScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
);
}
}
In flutter, you should never have constructors for states.
if you need to initialize state properties, use initState.

Related

how to pass a variable as a parameter to a widget two, modify it there, and return the modified value to widget one, Flutter

how to pass a variable as a parameter to a widget two, modify it there, and return the modified value to widget one.
I need to change the value of the variable when I click the "Change it" button, and that change is reflected in widget one.
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class FirstWidgetState extends State<FirstWidget> {
String c = 'start';
#override
Widget build(BuildContext context){
return Container(
child: SecondWidget(variable: c),
);
}
}
class SecondWidget extends StatefulWidget {
String variable;
SecondWidget({ this.variable });
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context){
return Container(
child: RaisedButton(child:Text('Change it'), onPressed: () {widget.variable = 'end';}),
);
}
}
It is possible to implement it easily with a callback, meaning you pass a function to your second widget, and when the button is clicked you call the function, this way you can modify whatever you want in the first widget.
Like this:
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class FirstWidgetState extends State<FirstWidget> {
String c = 'start';
#override
Widget build(BuildContext context){
return Container(
child: SecondWidget(variable: c, onChange: (newVal) {
setState(() {c = newVal;});
}),
);
}
}
class SecondWidget extends StatefulWidget {
String variable;
final onChange;
SecondWidget({ this.variable, this.onChange });
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context){
return Container(
child: RaisedButton(child:Text('Change it'), onPressed: () {widget.onChange('end');}),
);
}
}

How to implement Gesture detection - On Tap method in child class?

I want to implement the GestureDetector method onTapin child class. Is there a way to do it in Flutter ?
ParentClass.dart
Class ParentClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: ChildClass(),
}
}
ChildClass.dart
Class ChildClass extends StatefulWidget {
methodA() // need to access methodA which is being passed to gesture detector
// How do I access methodA of parent class method here
// so whenever GestureDetector's onTap method is called, i want to handle that in ChildClass is there a way to do it ?
}
You can access the Child State methods using a unique key. Here is a minimal example:
Inside the ParentWidget, we define _childKey, a GlobalKey<_ChildWidgetState> that we then can use to access the State's method updateValue as follows:
_childKey.currentState.updateValue('Goodbye, Thierry!'),
Full source code
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: ParentWidget()),
);
}
}
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
final _childKey = GlobalKey<_ChildWidgetState>();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _childKey.currentState.updateValue('Goodbye, Thierry!'),
child: ChildWidget(key: _childKey),
);
}
}
class ChildWidget extends StatefulWidget {
const ChildWidget({Key key}) : super(key: key);
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
String value = 'Hello, Thierry!';
void updateValue(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Text(value);
}
}
In your ChildClass, return a GestureDetector. Set the child property to the rest of your widgets, and then set the onTap to call methodA. That should look something like this:
class ChildClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: SomeWidget(),
}
}
You are asking how to detect child class onTap and pass it to Parent right?
class YourChild extends StatelessWidget {
final Function parentCallback;
const YourChild({this.parentCallback});
#override
Widget build(BuildContext context) {
return GestureDetector(
// 1st option
onTap: () {
print("do something");
parentCallback();
},
)
}
}
Then for using It.
class YourParent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return YourChild( parentCallback(){
//do your stuff}
)
}
}

How to set data in an inherited widget from a another widget?

So inherited widget is useful for passing data down the tree, but how do I set that data in the first place if inherited widgets are immutable?
I'm trying to set a phone number for OTP auth and then display that number on another screen. Provider is kind of advanced for me at the moment, how do I approach this?
thank you
You have to rebuild somewhere your InheritedWidget.
You can use any stage management for it, for example you can use StatefulWidget:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MyInheritedWidget extends InheritedWidget {
final int counter;
MyInheritedWidget({Key key, this.counter, Widget child})
: super(key: key, child: child);
#override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.counter != counter;
}
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MyInheritedWidget(counter: _counter, child: CounterWidget()),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_counter++;
});
},
),
);
}
}
class CounterWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("${MyInheritedWidget.of(context).counter}",
style: TextStyle(fontSize: 100));
}
}
Firstly you would use a StreamProvider for your stream of data (The same as you would using a StreamBuilder):
class Widget1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: Wrapper(),
);
}
}
Next widget has no required data
class Widget2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Widget3(),
);
}
}
Access your data via Provider.of
class Widget3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user == null) {
return Login();
} else {
return Dashboard();
}
}
}
With this method, you still need to access the data somewhere down the widget tree. You can't go up, if you want to have the ability to have a widget up the tree listen to something that happens down the tree, you will want to look at ChangeNotifier

Trigger animations on widget load flutter

So, I have a AnimatedPositioned widget in my widget tree, which contains a form. I want the AnimatedPositioned widget to slide up from bottom when user navigates to the screen. Now, there are many tutorials which show how to do this when user clicks a button using the setState method. But how do I trigger this automatically when this screen is loaded?
The "right" way to do that would be not to use an implicit animation widget like AnimatedPositioned and instead use an explicit animation. Where you start your AnimationController in the initState of a StatefulWidget.
But just technically you could set the "destination" values for your AnimatedPositioned widget in a Future so that the value changes on the next frame. You try remoing the Future to see that otherwise the widget renders at its end position.
This way is not recommended but possible:
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> {
double _dist = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
AnimatedPositioned(
child: Text('Hello Words'),
duration: Duration(seconds: 5),
left: _dist,
top: _dist,
),
]),
);
}
#override
initState() {
super.initState();
Future(() {
setState(() {
_dist = 250.0;
});
});
}
}

How to Overlay a Widget on top of another

I have an app that looks like this
class MainScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => MainScreenState();
}
class MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return new Container(
child: FlatButton(
onPressed:() {
// Want pop up to launch
},
),
)
}
}
class PopUp extends StatefulWidget {
PopUp({
this.postId
})
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> {
// api logic
#override
Widget build(BuildContext context) {
return new Dialog()
}
}
Is it possible for me to overlay the PopUp widget on top of the MainScreen Widget without using showDialog ? Since i'm trying to make the PopUp stateful in that it processes and rerenders itself depending on the processed data ?