I am developing a flutter plugin that instantiate objects in the dart code mapping other objects at the native side. I want to preserve memory by deleting objects at native code once their scope ends at the dart code.
If you are fine with the flutter framework managing your resources you could just wrap them within a State Object.
https://docs.flutter.io/flutter/widgets/State-class.html
The framework calls dispose() once the referring Stateful widget is permanently removed from the widget tree.
(1) Simple to implement - no control
class ResourceManager extends StatefulWidget {
#override
State<StatefulWidget> createState() => ResourceManagerState();
}
class ResourceManagerState extends State<ResourceManager>{
Resource referenceToResource;
#override
void initState() {
// create/open your unmanaged resource here
super.initState();
}
#override
void dispose() {
// close/destroy your resource here
super.dispose();
}
#override
Widget build(BuildContext context) {
return SomeWidgetThatUsesTheResource();
}
}
Things do get more complicated if you need to have more control over the resource. The garbage collector does not release the resource exactly when the Widget goes out of scope but only sometime later (or maybe only when the application is terminated)
Alternatively you could manage your resource using the didChangeDependencies and deactivate methods of the State object. I'd expect these to be called more reliably and closer in time to the actual scope change of the resource:
(2) still simple - slightly more control
class ResourceManager extends StatefulWidget {
#override
State<StatefulWidget> createState() => ResourceManagerState();
}
class ResourceManagerState extends State<ResourceManager>{
Resource referenceToResource;
#override
void didChangeDependencies() {
if(referenceToResource = null) {
// open/create resource here
}
super.didChangeDependencies();
}
#override
void deactivate() {
// close/destroy your resource here
super.deactivate();
}
#override
Widget build(BuildContext context) {
return SomeWidgetThatUsesTheResource();
}
}
(3) requires some work - even more control
If you need to reliably manage a limited number of resources you might have a look at the pool plugin for Flutter:
https://pub.dartlang.org/packages/pool
Related
If I try to creat an Doc with dartdoc on an Flutter Application I get
[error] Invalid override. The type of 'AddLinkPage.createState' ('() → AddLinkPageState') isn't a subtype of 'StatefulWidget.createState' ('() → State')
and
[error] Classes can only extend other classes.
class AddLinkPage extends StatefulWidget {
AddLinkPage({Key key,}):super(key: key);
#override
AddLinkPageState createState() {
return AddLinkPageState();
}
}
class AddLinkPageState extends State<AddLinkPage>{
...
If I change my code to
class AddLinkPage extends StatefulWidget {
AddLinkPage({Key key,}):super(key: key);
#override
createState() {
return AddLinkPageState();
}
}
class AddLinkPageState extends State<AddLinkPage>{
...
I avoid the first error but the second still there and the generation of the dartdoc failed.
Is the dartdoc only for packes, plugin, module without application or do I something wrong?
This question already has an answer here:
Dart: should the instance variables be private or public in a private class?
(1 answer)
Closed 4 years ago.
In Flutter we commonly have something like this:
class MyStatefulWidget extends StatefulWidget {
#override
_MyState createState() => _MyState();
}
class _MyState extends State<MyStatefulWidget> {
void doSomething() => print('hi');
#override
Widget build(BuildContext context) {
return Container();
}
}
So _MyState is declared with a _, which makes it library private.
So how come the Flutter render engine can use _MySate if it's sopposed to be private?
It's funny because I can access doSomething() from other files, but if I make it _doSomething(), I can't access it anymore...So how come I can access a public method from a private class, but I can't access a private method from a private class?
While _MyState is private, StatefulWidget and State are not.
The framework doesn't manipulate _MyState, it manipulates these lower layer that he has access to, with a well-defined prototype.
This basically sums up into:
StatefulWidget widget;
State foo = widget.createState();
foo.initState();
final newWidget = foo.build(this);
...
I am using the BLoC pattern as described at the Google IO talk.
I have a simple BLoC which is used to display a alert in the UI whenever a string is added to messageSink:
class AlertBloc {
final _message = BehaviorSubject<String>();
AlertBloc() {}
Stream<String> get message => _message.stream;
Sink<String> get messageSink => _message.sink;
void dispose() {
_message.close(); }
}
Elsewhere in the app, I have another BLoC which needs to add a string to messageSink, when a certain condition is met.
I noticed it is not a good idea to provide the whole BLoC from the Google I/O repo for the talk, and they provide advice for connecting a stream from a BLoC to another BLoC sink:
Note that we are not providing [CartBloc] to the
[ProductSquareBloc] directly, although it would be easier to
implement. BLoCs should not depend on other BLoCs (separation of
concerns). They can only communicate with each other using
streams. In this case, the [CartBloc.items] output plugs into the
[ProductSquareBloc.cartItems] input.
My question is how to connect a sink from a BLoC to another BLoC stream?
Here is a simple example for you. Imagine the following two BLoCs:
The first one exposes a Stream and populates it with some values:
class ProducerBLoC {
//Controller is private - you do not want to expose it
final StreamController<int> _productionController = StreamController<int>();
//Instead, you expose a stream
Stream<int> get production => _productionController.stream;
//This method generates some values and puts them to stream
void produceValue() {
_productionController.sink.add(1);
_productionController.sink.add(2);
_productionController.sink.add(3);
}
//Don't forget to close your controllers
void dispose() {
_productionController.close();
}
}
The other one exposes a Sink and processes values that are put into it.
class ConsumerBLoC {
//Controller is private - you do not want to expose it
final StreamController<int> _consumptionController = StreamController<int>();
//Instead, you expose a sink
StreamSink<int> get consumption => _consumptionController.sink;
//In class constructor we start listening to the stream of values
ConsumerBLoC() {
_consumptionController.listen((value) {_consumeValue(value);} );
//or simply: _consumptionController.listen(_consumeValue); //theese are the same
}
//This method generates some values and puts them to stream
void consumeValue(int value) {
//Do something with the value
print('Value processed: $value');
}
//Don't forget to close your controllers
void dispose() {
_consumptionController.close();
}
}
Now, the task is to connect production stream to consumption sink. As you have correctly noticed, you do not want for any of two BLoCs to know anything about existence of the other one. So none of the two should hold references to the other one or even create instances of another one. Instead, you connect them using your Widget class:
//Define some widget to represent main screen of your application
class MainScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MainScreenState();
}
//And define a state for this widget (state does not need to be public)
class _MainScreenState extends State<MainScreen> {
//You define both blocks here
ProducerBLoC _producer = new ProducerBLoC();
ConsumerBLoC _consumer = new ConsumerBLoC();
//Now, either do it in _MainScreenState constructor, or in the initState() method
#override
void initState() {
super.initState();
//Connect production stream with consumption sink
_producer.production.listen((value) => _consumer.consumption.add(value));
//Or, beautifully: _producer.production.pipe(_consumer.consumption);
}
#override
Widget build(BuildContext context) {
//The exact implementation does not matter in current context
}
//And don't forget to close your controllers
#override
dispose() {
super.dispose();
_producer.dispose();
_consumer.dispose();
}
}
This way, any value generated by ProducerBLoC will immediately be consumed by ConsumerBLoC. And, what's the most important, - both BLoCs are completely independent from one another!
The exact same way as you'd do with streams: Passing it as parameter
class Bloc {
final Sink<int> _external;
Bloc(this._external);
}
I'm new to Flutter and I'm trying to accomplish a simple thing:
I want to create a signup functionality using BLoC pattern and streams.
For the UI part I have a stepper, that on the very last step should fire a request to the server with the collected data.
I believe I have everything working until the StreamBuilder part. StreamBuilders are meant to return Widgets, however, in my case I don't need any widgets returned, if it's a success I want to navigate to the next screen, otherwise an error will be displayed in ModalBottomSheet.
StreamBuilder is complaining that no widget is returned.
Is there anything else that could be used on the View side to act on the events from the stream?
Or is there a better approach to the problem?
If you don't need to render anything, don't use StreamBuilder to begin with.
StreamBuilder is a helper widget used to display the content of a Stream.
What you want is different. Therefore you can simply listen to the Stream manually.
The following will do:
class Foo<T> extends StatefulWidget {
Stream<T> stream;
Foo({this.stream});
#override
_FooState createState() => _FooState<T>();
}
class _FooState<T> extends State<Foo<T>> {
StreamSubscription streamSubscription;
#override
void initState() {
streamSubscription = widget.stream.listen(onNewValue);
super.initState();
}
void onNewValue(T event) {
Navigator.of(context).pushNamed("my/new/route");
}
#override
void dispose() {
streamSubscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
In the following code example, from the flutter docs:
class RandomWords extends StatefulWidget {
#override
createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
#override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
}
What exactly does the State<RandomWords> syntax mean?
I understand that you can specify the type for the objects contained in a collection, like lists, using this syntax - List <String>
But I cannot understand the motive behind State<RandomWords>.
Moreover, how can you reference RandomWordsState in RandomWords declaration and also reference RandomWords in RandomWordsState declaration? Shouldn't that cause a circular reference error or something?
I come from dynamically typed languages like python, and this looks a little odd to me, can someone please point me to the right place?
<RandomWords> is a generic type parameter passed to the State class.
The State class looks like
abstract class State<T extends StatefulWidget> extends Diagnosticable {
and RandomWords will be passed to the T type parameter which has a constraint that T needs to be a subclass of StatefulWidget.
State also has a field and getter where the type parameter is used
T get widget => _widget;
T _widget;
This results in a property of the type of the widget
which provides proper autocompletion and type checks in its subclass RandomWordsState
Assume you have
class RandomWords extends StatefulWidget {
RandomWords({this.fixed});
final WordPair fixed;
#override
createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
#override
Widget build(BuildContext context) {
// vvvv here we can access `fixed` in a strongly typed manner
final wordPair = widget.fixed ?? WordPair.random();
return Text(wordPair.asPascalCase);
}
}
See also https://www.dartlang.org/guides/language/language-tour#generics