I have this function in a widget (homescreen):
void toggleRecording() async {
// HERE IS THE CONFUSION I GUESS
_isRecording = !_isRecording;
recorder = SoundStream(isRecording: _isRecording);
//recorder.toggleRecording(_isRecording);
setState(() {
_isRecording = recorder.isRecording;
});
if (_isRecording) {
startTimer();
_stopwatch.start();
} else {
stopTimer();
_stopwatch.stop();
}
}
It needs to call (trigger) another function in my recorder class:
void toggleRecording() async {
widget.isRecording ////// currently being passed as an argument from homescreen
? {_recorder.stop, await save(_micChunks, 44100)}
: _recorder.start;
}
Also, the boolean variable _isRecording is present in both the classes. How do I sync the state?
In your situation passing reference of function through widgets will work. However best practice of this will be using provider package.
Managing functions from provider will allow you to control functions from all pages.
If you change a variable you can call notifylistener() function inside your provider function. So that you can change state of widget.
I will try to explain it in a glance however this is an important subject of flutter.
Here is my folder structure
At provider folder we define our provider classes.
Firstly i define class which extends changeNotifier class. This is what make this class provider.
Side note: notifyListener() function here calls setState of every widget if you use any variables inside that class and this is what you are searching for.
Then i import it into my main.dart file or whatever file you want. Only condition is being above the widget that you will use provider at.
At last you can use your function at everywhere if you import provider package and define your provider like i did in this code.
At last here is the visualized stucture of provider package.
I wish i explained it well. There is more about it on youtube.
Pass the function to other widget
using Function keyword
Say ContentWidget is your child and ParentWidget is parent
class ParentWidget extends StatefulWidget {
//Do Something
void onSomeFunction()
{
ContentWidget(onTimerUpdate:onTimerClosed)
}
void onTimerClosed()
{
//Perform Operation on Timer Change
}
}
class ContentWidget extends StatefulWidget {
final Function onTimerUpdate;
ContentWidget({
Key key,
#required this.onTimerUpdate,
}) : super(key: key);
void onAnyActionFromChild()
{
widget.onTimerUpdate() //Note () can have any param or can be null
}
I want to rebuild the buildSuggestions method that returns a widget that the defined below
and resultInfo getting the result from TMDB API search that a string as an input
how can i recall the resultInfo and rebuild it.
#override
Widget buildSuggestions(BuildContext context) {
if (query.length > 1) {
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
//ResultInfo not updating after the query updates
}
this is the home screen code where searchdelegate is defined and here you can find the code for ResultInfo widget.
UPDATE: After looking at the project, you need to call the getMovies function from the buildSuggestions build method because initState was only getting called once despite the rebuilds. Add this and you're good.
#override
Widget buildSuggestions(BuildContext context) {
if (query != '') {
searchResult.getMovies(query); // do the search from here
return ResultInfo(searchString: query);
}
return ResultInfo(searchString: "old");
}
}
You can leave the search in initState also so you don't get a blank list when you navigate to the search page.
OLD ANSWER:
Any time the query changes in a widget that extends SearchDelegate, buildSuggestions is called. You can verify that by adding a print statement in the buildSuggestions build method.
I see a couple issues here:
class ResultInfo extends StatefulWidget {
final String s;
ResultInfo({Key key, #required this.s}) : super(key: key);
#override
_MovieInfoState createState() => _MovieInfoState(s);
}
class _MovieInfoState extends State<ResultInfo> {
// final String s; don't need this here
_MovieInfoState(this.s);
#override
void initState() {
super.initState();
searchResult.getMovies(widget.s); // access the actual parameter you passed in with widget.s
}
When you pass a value into a stateful widget you don't re-declare the same value again in the stateful part, you access what was passed in with widget.variable. So in your case you were passing in a null value into the getMovies function.
So assuming your stream functionality is all working, that simple change alone should fix your issue.
And in the interest of your code being readable by other people, I suggest a better variable name than s, like query for example because that's what it is. Anyone reading your code has no idea what s is and what it's for until they dig around a bit.
Second issue:
if (query.length > 1) { // won't return results until 2nd character is input
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
}
Maybe its your intention to only return ResultInfo(s: query) until more than one character is input, but if its not, change your condition to this
if (query != '') {
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
}
Am trying to connect on a SignalR server with flutter, and I tried to use Provider to access the data which comes to another Widget, but am receiving a null value. This is the SignalR library for flutter am using.
Below is my Class which extends a ChangeNotifier :
class BreakingNewsSignalRClient with ChangeNotifier {
String _breakingNews;
Future<void> getBreakingNews() async {
final hubConnection = HubConnectionBuilder()
.withUrl('https://services.xxxx.com/breakingNews')
.build();
await hubConnection.start();
hubConnection.on("UpdateBreakingNews", (data){
_breakingNews = data.toString();
print(data.toString());
notifyListeners();
});
hubConnection.onclose((error) => print("Connection Closed"));
}
String get breakingNewsFunction => _breakingNews;
}
class BreakingNewsModel {
final String News;
BreakingNewsModel(this.News);
}
As you can see the code above, am having a getter String get breakingNewsFunction => _breakingNews;, this is called in another Stateful Widget to populate some data from the SignalR server, but it returns null in the other widget, however, when you try to print data here in the getBreakingNews method the data is shown.
Below is another Widget class which receives the data :
class BreakingNews extends StatefulWidget {
const BreakingNews({Key key, #required this.article, #required this.index})
: super(key: key);
final Article article;
final int index;
#override
_BreakingNewsState createState() => _BreakingNewsState();
}
class _BreakingNewsState extends State<BreakingNews> {
settings() async{
var breakingNewsInfo =
Provider.of<BreakingNewsSignalRClient>(context, listen: false);
await breakingNewsInfo.getBreakingNews();
print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');
}
#override
void initState() {
// TODO: implement initState
settings();
super.initState();
}
}
So when you look at this line print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');, it prints null, am still wondering what am doing wrong here.
Kindly need some help.
Did you try data[0]?
Can you write it?
_breakingNews = data[0].toString();
instead of
_breakingNews = data.toString();
This is pretty simple. Your Future<void> getBreakingNews() async returns void and therefore null. Just adjust the return type to whatever you need.
Edit:
Actually, the problem is you are not calling your getter here.
await breakingNewsInfo.getBreakingNews();
So either return your result from the function, or call the getter. Either should work.
I am trying to get a list from class to the other. But I want it to only be transferred after it has got a value assigned from a Future. Is there a way to do so (something like a setState method that acts across classes) My code is here:
class Design extends StatefulWidget {
#override
_DesignState createState() => _DesignState();
}
class _DesignState extends State<Design>{
var Data;
#override
void initState() {
super.initState();
comparer().then((List returnedV){
setState(() {
Data = returnedV;
});
});
}
Future<List> compare() async {
...
return dataDevice
}
}
class AboutSheet extends StatefulWidget {
final List Data;
AboutSheet({#required this.Data});
#override
_AboutSheetState createState() => _AboutSheetState();
}
class _AboutSheetState extends State<AboutSheet> {
}
Every time I use the variable Data in the second class it has the value null. I think it's because I have defined it before with the value null and it's pulling that and is not waiting for the future to assign a value to it. I can't think of a workaround. I would really appreciate your help!
What you are referring to is a state management solution. There is a lot of them, with each their pros and cons. I (and the Flutter team) would suggest Provider.
Take a look at this : List of state management approaches
I'm trying to understand the best practice for controlling a StatefulWidget's state outside of that Widgets State.
I have the following interface defined.
abstract class StartupView {
Stream<String> get onAppSelected;
set showActivity(bool activity);
set message(String message);
}
I would like to create a StatefulWidget StartupPage that implements this interface. I expect the Widget to do the following:
When a button is pressed it would send an event over the onAppSelected stream. A controller would listen to this event and perform some action ( DB call, service request, etc ).
The controller can call showActivity or set message to have the view show progress with a message.
Because a Stateful Widget does not expose its State as a property, I don't know the best approach for accessing and modifying the State's attributes.
The way I would expect to use this would be something like this:
Widget createStartupPage() {
var page = new StartupPage();
page.onAppSelected.listen((app) {
page.showActivity = true;
//Do some work
page.showActivity = false;
});
}
I've thought about instantiating the Widget by passing in the state I want it to return in createState() but that feels wrong.
Some background on why we have this approach: We currently have a Dart web application. For view-controller separation, testability, and forward-thinking towards Flutter, we decided that we would create an interface for every view in our application. This would allow a WebComponent or a Flutter Widget to implement this interface and leave all of the controller logic the same.
There are multiple ways to interact with other stateful widgets.
1. findAncestorStateOfType
The first and most straightforward is through context.findAncestorStateOfType method.
Usually wrapped in a static method of the Stateful subclass like this :
class MyState extends StatefulWidget {
static of(BuildContext context, {bool root = false}) => root
? context.findRootAncestorStateOfType<_MyStateState>()
: context.findAncestorStateOfType<_MyStateState>();
#override
_MyStateState createState() => _MyStateState();
}
class _MyStateState extends State<MyState> {
#override
Widget build(BuildContext context) {
return Container();
}
}
This is how Navigator works for example.
Pro:
Easiest solution
Con:
Tempted to access State properties or manually call setState
Requires to expose State subclass
Don't use this method when you want to access a variable. As your widget may not reload when that variable change.
2. Listenable, Stream and/or InheritedWidget
Sometimes instead of a method, you may want to access some properties. The thing is, you most likely want your widgets to update whenever that value changes over time.
In this situation, dart offer Stream and Sink. And flutter adds on the top of it InheritedWidget and Listenable such as ValueNotifier. They all do relatively the same thing: subscribing to a value change event when coupled with a StreamBuilder/context.dependOnInheritedWidgetOfExactType/AnimatedBuilder.
This is the go-to solution when you want your State to expose some properties. I won't cover all the possibilities but here's a small example using InheritedWidget :
First, we have an InheritedWidget that expose a count :
class Count extends InheritedWidget {
static of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<Count>();
final int count;
Count({Key key, #required Widget child, #required this.count})
: assert(count != null),
super(key: key, child: child);
#override
bool updateShouldNotify(Count oldWidget) {
return this.count != oldWidget.count;
}
}
Then we have our State that instantiate this InheritedWidget
class _MyStateState extends State<MyState> {
int count = 0;
#override
Widget build(BuildContext context) {
return Count(
count: count,
child: Scaffold(
body: CountBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
count++;
});
},
),
),
);
}
}
Finally, we have our CountBody that fetch this exposed count
class CountBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text(Count.of(context).count.toString()),
);
}
}
Pros:
More performant than findAncestorStateOfType
Stream alternative is dart only (works with web) and is strongly integrated in the language (keywords such as await for or async*)
Automic reload of the children when the value change
Cons:
More boilerplate
Stream can be complicated
3. Notifications
Instead of directly calling methods on State, you can send a Notification from your widget. And make State subscribe to these notifications.
An example of Notification would be :
class MyNotification extends Notification {
final String title;
const MyNotification({this.title});
}
To dispatch the notification simply call dispatch(context) on your notification instance and it will bubble up.
MyNotification(title: "Foo")..dispatch(context)
Note: you need put above line of code inside a class, otherwise no context, can NOT call notification.
Any given widget can listen to notifications dispatched by their children using NotificationListener<T> :
class _MyStateState extends State<MyState> {
#override
Widget build(BuildContext context) {
return NotificationListener<MyNotification>(
onNotification: onTitlePush,
child: Container(),
);
}
bool onTitlePush(MyNotification notification) {
print("New item ${notification.title}");
// true meaning processed, no following notification bubbling.
return true;
}
}
An example would be Scrollable, which can dispatch ScrollNotification including start/end/overscroll. Then used by Scrollbar to know scroll information without having access to ScrollController
Pros:
Cool reactive API. We don't directly do stuff on State. It's State that subscribes to events triggered by its children
More than one widget can subscribe to that same notification
Prevents children from accessing unwanted State properties
Cons:
May not fit your use-case
Requires more boilerplate
You can expose the state's widget with a static method, a few of the flutter examples do it this way and I've started using it as well:
class StartupPage extends StatefulWidget {
static StartupPageState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<StartupPageState>());
#override
StartupPageState createState() => new StartupPageState();
}
class StartupPageState extends State<StartupPage> {
...
}
You can then access the state by calling StartupPage.of(context).doSomething();.
The caveat here is that you need to have a BuildContext with that page somewhere in its tree.
There is another common used approach to have access to State's properties/methods:
class StartupPage extends StatefulWidget {
StartupPage({Key key}) : super(key: key);
#override
StartupPageState createState() => StartupPageState();
}
// Make class public!
class StartupPageState extends State<StartupPage> {
int someStateProperty;
void someStateMethod() {}
}
// Somewhere where inside class where `StartupPage` will be used
final startupPageKey = GlobalKey<StartupPageState>();
// Somewhere where the `StartupPage` will be opened
final startupPage = StartupPage(key: startupPageKey);
Navigator.push(context, MaterialPageRoute(builder: (_) => startupPage);
// Somewhere where you need have access to state
startupPageKey.currentState.someStateProperty = 1;
startupPageKey.currentState.someStateMethod();
I do:
class StartupPage extends StatefulWidget {
StartupPageState state;
#override
StartupPageState createState() {
this.state = new StartupPageState();
return this.state;
}
}
class DetectedAnimationState extends State<DetectedAnimation> {
And outside just startupPage.state
While trying to solve a similar problem, I discovered that ancestorStateOfType() and TypeMatcher have been deprecated. Instead, one has to use findAncestorStateOfType(). However as per the documentation, "calling this method is relatively expensive". The documentation for the findAncestorStateOfType() method can be found here.
In any case, to use findAncestorStateOfType(), the following can be implemented (this is a modification of the correct answer using the findAncestorStateOfType() method):
class StartupPage extends StatefulWidget {
static _StartupPageState of(BuildContext context) => context.findAncestorStateOfType<_StartupPageState>();
#override
_StartupPageState createState() => new _StartupPageState();
}
class _StartupPageState extends State<StartupPage> {
...
}
The state can be accessed in the same way as described in the correct answer (using StartupPage.of(context).yourFunction()). I wanted to update the post with the new method.
You can use eventify
This library provide mechanism to register for event notifications with emitter
or publisher and get notified in the event of an event.
You can do something like:
// Import the library
import 'package:eventify/eventify.dart';
final EventEmitter emitter = new EventEmitter();
var controlNumber = 50;
List<Widget> buttonsGenerator() {
final List<Widget> buttons = new List<Widget>();
for (var i = 0; i < controlNumber; i++) {
widgets.add(new MaterialButton(
// Generate 10 Buttons afterwards
onPressed: () {
controlNumber = 10;
emitter.emit("updateButtonsList", null, "");
},
);
}
}
class AState extends State<ofYourWidget> {
#override
Widget build(BuildContext context) {
List<Widget> buttons_list = buttonsGenerator();
emitter.on('updateButtonsList', null, (event, event_context) {
setState(() {
buttons_list = buttonsGenerator();
});
});
}
...
}
I can't think of anything which can't be achieved by event driven programming. You are limitless!
"Freedom cannot be bestowed — it must be achieved."
- Elbert Hubbard
Have you considered lifting the state to the parent widget? It is a common, though less ideal than Redux, way to manage state in React as far as I know, and this repository shows how to apply the concept to a Flutter app.