How to re-render the whole Flutter app on some event? - flutter

Is there a recommended way how to force the whole Flutter app to re-render during runtime but without losing state?
I need to update the app according to the back-end response, and I can't use the regular approaches (updating the widget state, inherited widget - provider pattern, etc.). More precisely, the data I need to update is shown on the UI via one Flutter package that does not provide the ability to trigger re-rendering in case of change (data is loaded just once on startup).
In other words, is the below-posted solution valid? Are there any known drawbacks of this approach? Can I crash the app forcing re-rendering this way?
import 'package:flutter/material.dart';
main() {
runApp(Wrapper(child: MyApp()));
}
class Wrapper extends StatelessWidget {
final Widget child;
Wrapper({Key? key, required this.child}) : super(key: key);
updateIfNeeded(BuildContext context) {
// faking some API call...
Future.delayed(Duration(seconds: 5), () {
void rebuild(Element el) {
el.markNeedsBuild(); // can something go wrong here?
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
});
}
#override
Widget build(BuildContext context) {
updateIfNeeded(context);
return child;
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Wrapper(
child: MaterialApp(
title: 'Flutter App',
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text('Some content...')],
),
),
),
),
);
}
}

You can use a global key and assign it to MaterialApp key and change it's value whenever you want to refresh the app

Related

How to interact with a child Stateful Widget from a parent Stateless Widget?

Context :
Say I have a custom DisplayElapsedTime Widget which is Stateful and holds a Text Widget. With a Timer, every 1sec, the text value is updated with the current elapsed time given by a Stopwatch.
And now, say I have a page which is Stateless and has the DisplayElapsedTime Widget as a child.
What I would like to do :
On the click of a "Start" button in my page, I would like to start the DisplayElapsedTime (which means starting the Stopwatch and the Timer).
From my page, I would also like to have access to the elapsed time value of the Stopwatch "whenever I want".
Why I am having a hard time :
So far (see: I use Stateless Widget alongside BLoC almost everytime. Am I wrong?), I have almost always worked with Stateless Widget alongside the pattern BLoC and never used Stateful. Currently, having extremely long and complex Widgets, I am starting to sense the "limits" of not using the better of the two worlds. But I don't quite fully understand how the Widgets should be interacting between one another.
I really cannot find the solution to my problem anywhere (or am really bad at searching). However, surely, I cannot be the first person to want to have "control" over a Stateful Widget from a Stateless Widget, right ?
Thank you so much in advance for any help.
If I understand your question correctly, let me try to explain this using the most familiar app of all time, the beginning counter app.
This snippet contains a single StatefulWidget that controls its ability to rebuild using its setState method _incrementCounter. So, the value is incremented and the widget is rebuilt whenever the StatefulWidget calls the setState method inside itself.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
A StatefulWidget can fully rebuild itself, and when doing so, it may also rebuild all its children downstream of it in the widget tree (but not always, as const widgets are not rebuilt). To get another widget to rebuild a parent widget (upstream of it in the widget tree), you need to have that StatefulWidget's setState function. This can be done using a callback function. A callback function is made by the parent widget and passed to a child widget. So, in the following example, I have made a StatelessWidget with a button, which controls its parent widget because it calls its parent's callback function; notice that I give:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
and not:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter()),
Passing _incrementCounter() with the parenthesis calls it at the moment it is passed, while _incrementCounter allows it to be called downstream in the widget tree.
Use the callback function in the child widget by calling it anywhere (notice the parentheses).
onPressed: () {
fx();
},
Here is the new code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
);
}
}
class ExampleStlessWidget extends StatelessWidget {
const ExampleStlessWidget({
super.key,
required int counter,
required this.fx,
}) : _counter = counter;
final int _counter;
final Function fx;
#override
Widget build(BuildContext context) {
return Column(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
ElevatedButton(
onPressed: () {
fx();
},
child: const Text('Click me'),
),
],
);
}
}
A bloc involves an inherited widget, which allows for monitoring the state throughout the widget tree and rebuilds widgets depending on that state. So, a bloc doesn't need a StatefulWidget to change UI. It would help if you did not look at one tool's ability to rebuild widgets as bad or good. It would be best to look at StatefulWidgets and BLoC as different tools for different jobs.
I hope this helps. Happy coding.

Segmenting the elements of homepage in multiple >dart files

Being new to Flutter I want to know if it is a good practice to segregate the elements of Any page like HOME to different Classes or DART files.
If the answer is positive, I need some help with that.
I am aware that I have to Include the pages in both Mother and daughter .dart pages to each other.
Where I am confused is how much should I mention for a part of a page. (please forgive my nativity if there any)
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AppName',
home: MyHomePage(),
);
What should I return?
The Material App already runs the Mother or main page so how much to be included?
Or should I just Code the elements Like Row and Column and Card etc...
If the latter is true then how should I call them? Will those be automatically called when The MAIN .dart is executed?
~Addition~
Can I return any Layout Widget(Row/Column/Card) out of nothing !!
like
class MyHomePage extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
(I think it is logical because all the queries will be ultimately forwarded to MAIN.dart)
Any help is appreciated.
If I understand your question correctly, let me answer with an example:
Say your main.dart is as follows:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AppName',
home: MyHomePage(),
);
}
}
and your home_page.dart is:
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AppName"),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
CustomWidget1(),
CustomWidget2(),
],
),
);
}
}
Then CustomWidget1 can be (in a file named custom_widget_1.dart):
class CustomWidget1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("CustomWidget1"),
);
}
}
Then CustomWidget2 can be (in a file named custom_widget_2.dart):
class CustomWidget2 extends StatefulWidget {
CustomWidget2({Key key}) : super(key: key);
#override
_CustomWidget2State createState() => _CustomWidget2State();
}
class _CustomWidget2State extends State<CustomWidget2> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("CustomWidget2"),
);
}
}
Yes you can create many directories and arrange your Dart files in it like services, model and config. As you call the main.dart the other Classes will not certainly be on main.dart, let me put this this way, maon.dart = >homepage.dart => productPage.dart=>.......
it is just navigation while navigation to some parameters classes be sure to parse the parameters

How to call a Stateless widget in MyApp's build method whose state is managed by its Stateful Parent

I was following flutter tutorials for managing state of a widget from its parent on this link [https://flutter.dev/docs/development/ui/interactive#parent-managed][1]
and i cant figure out how would call the widget in this case
it is very simple once you get the logic.
In practice, the parent (the "true" widget that you call), i.e.
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
is the one that you call wherever and whenever you want in the rest of the code.
Since this is a Stateful widget, it means that it has stated (to keep it simple, it will manage any changes on the UI). Any change will occur, It will be changing its state and so, this code:
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
Anyhow, once you use a Stateful widget, you change its state whenever you want to call the function
setState(() {
oldValue= newValue;
});
It will rebuild the entire widget changing the stuff you want (such as texts, images, widgets, and so on).
In a non-proper way, consider it as a particular widget that can change its UI during the time.
if you want to call it in MyApp's build method you will have to make MyApp a stateful widget so that it can manage the state of the said widget
void main() => runApp(MyApp());
//we make MyApp to be a stateful widget
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
//we define the state which will be used in the widget here
var myState = "something";
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
//the data used by MyWidget is managed by MyApp which is a statefull widget.
child: MyWidget(state: myState),
),
),
),
);
}
}
Or rather wrap your widget with another stateful widget which you will use in MyApp's build method
//we create a widget which will manage the state of its children class MyStateManagingWidget extends StatefulWidget { #override
_MyStateManagingWidgetState createState() => _MyStateManagingWidgetState(); }
class _MyStateManagingWidgetState extends State<MyStateManagingWidget> { var myState = "some state"; #override Widget build(BuildContext context) {
//we put our widget who's state is to be managed here
return MyWidget(); } }
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
//we now use the state managing widget here
child: MyStateManagingWidget()),
),
),
); } }

Navigate between pages using Navigator issue

I am studying Flutter and building my first app using this framework.
Now I am facing a problem.
My scenario is very simple I want to navigate from the main screen to another screen.
this is the code of the from the home view
class HomeView extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return HomeViewState();
}
}
class HomeViewState extends State<HomeView> {
...
and I want to navigate to to another screen using Navigator
#override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: () {
Navigator.of(context).pushNamed('/userdetailsview');
},
child: Card(
...
this is my App.Dart
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
home: Scaffold(
body: Center(
child: HomeView(),
),
),
routes: <String,WidgetBuilder>{
'/homeview': (BuildContext context) => new HomeView(),
'/userdetailsview': (BuildContext context) => new UserDetails(),
},
);
}
}
finally this is the code for the page I want to navigate
class UserDetails extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Text('test');
}
}
As you can see my scenario is very simple but this is the result .
As you can see for some reason the second page is overlapping the main page.
I am developer using Xamarin Forms and XAML applications Flutter is very easy to understand and I really like it but there is a lack of information about simple task like this one.
I would appreciate if someone could help to fix my issue
Thank you!.
Try this in UserDetails.dart
class UserDetails extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Text('test');
)
}
}

Call method of a widget from another widget

I just started flutter, this is a basic question but I am not able to solve.
I created a stateful widget and I need to call the setState() method on click of a button. The button is not part of this stateful widget. The button is present in the footer of application.
complete application code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
build(BuildContext context) {
return new MaterialApp(
title: "My app title",
home: new Scaffold(
appBar: new AppBar(
title: new Text("My App"),
backgroundColor: Colors.amber,
),
body: new Container(
child: new Center(
child: new MyStateFullWidget(),
),
),
persistentFooterButtons: <Widget>[
new FlatButton(
onPressed: () {
// I need to call the update() of MyStateFullWidget/MyStateFullWidgetState class
},
child: new Text("Click Here"),
color: Colors.amber,
textColor: Colors.white,
),
],
));
}
}
class MyStateFullWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new MyStateFullWidgetState();
}
}
class MyStateFullWidgetState extends State<MyStateFullWidget> {
int count = 0;
#override
Widget build(BuildContext context) {
return new Text("Count: $count");
}
update() {
setState() {
count++;
}
}
}
I need to call the setState() method on click of a button
You may have a few options (or alternatives) to achieve the end result (all with different tradeoffs):
Elevate the state (i.e. count) to an Inherited Widget above the Button and Display Widget. This may be the easiest or most appropriate.
Leverage some kind of Action-based communication such as Flutter Redux (where you dispatch an action, which affects the display widget via a StoreConnector and rebuilds). This can be seen as just another way to 'elevate' state. However, this requires a whole new dependency and a lot of overhead given your simple example, but I wanted to point it out.
You can create some kind of Stream and StreamController that the Display widget subscribes/listens to. However, this may be overkill and I'm not sure how appropriate representing button clicks over a stream would be.
There may be other solutions that I'm not thinking of; however, remember that the goal of reactive UI is to keep state simple.
So if you have multiple leaf widgets that care about a piece of state (want to know it, want to change it), it might mean that this belongs to a higher level component (e.g a App-level piece of state, or maybe some other common ancestor - but both could use Inherited Widget to prevent passing the piece of state all around)
You should use your Scaffold in the State instead of using in StatelessWidget.
Here is the working solution.
class MyApp extends StatelessWidget {
#override
build(BuildContext context) {
return MaterialApp(
title: "My app title",
home: MyStateFullWidget());
}
}
class MyStateFullWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyStateFullWidgetState();
}
}
class MyStateFullWidgetState extends State<MyStateFullWidget> {
int count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My App"),
backgroundColor: Colors.amber,
),
body: Container(
child: Center(
child:Text("Count: $count"),
),
),
persistentFooterButtons: <Widget>[
FlatButton(
onPressed: update,
child: Text("Click Here"),
color: Colors.amber,
textColor: Colors.white,
),
],
);
}
void update() {
setState(() {
count++;
});
}
}