I'm building an app that has text to speech.
It's possible to change the voice and set speed and such.
I'm using a standard TTS from the public dev.
My question is, how to initialize this and use it throughout the app without passing recreating it everywhere since the object rarely will change.
Once it is set up, I just want to have it speak.
It's certainly not impossible to recreate it every time I need it.
I'm looking for best practices.
Just have a class that holds all the information with an initialize method:
class Constants {
static bool firstVar;
static String secondVar;
static void initialize() {
firstVar = true;
secondVar = "";
}
}
// then call it in your main method before building the first widget
Constants.initialize();
In this case, you need to use a state management architecture.
There are a good number of them to choose from. There are many articles and videos that explain how to use them. One thing they have in common is just what you're looking for. The ability to provide state at an app-wide level without reconfiguring in each widget.
Check out the Flutter Documentation here. Follow through the next pages and you'll have a good general overview of state management. Available options include, Provider, Riverpod, Redux, InheritedWidget, Stacked, etc. (Many of them actually).
For example, with Provider, you initialise the TTS in some TTS StateProvider (just an example). Then after wrapping your topmost MaterialApp in a ProviderScope, you can easily use the TTS in any widget as follows:
final tts = Provider.of<TTSProvider>(context).tts;
tts.doWhateverYouWant();
Another advantage of these architectures is that a good number of them permit good separation of concerns and dependency injection (or inversion of control).
In simpler terms, separation of concerns give you have the ability to write UI specific code separate from logic specific code. This way, if you want to debug, or change the packages or APIs you're using, you'll do them easily from one place (without fear of damaging the codebase). Besides it promotes clean code too.
Dependency Injection involves the use of services or something similar to obtain what widgets need to work without the widgets configuring the services themselves.
As you explore, you'll also notice that some architectures like Stacked permit you to manage app-wide state without the BuildContext. For another example for TTS, with Stacked Architecture, you could have:
final tts = locator<TTSService>().tts;
tts.doWhateverYouWant();
This pattern is useful because, in StatelessWidgets, the BuildContext is available only within the build method and you might need to use stuff outside it. In addition to obtaining the TTS anywhere in the Flutter code, you could do other things like BottomSheet, Navigation, Toast, etc. without BuildContext.
Furthermore, you'll notice that you will tend to use more StatelessWidgets as widgets no longer keep state for you.
On a final note, don't worry about performance. Flutter in itself is efficient and handles everything properly.
Related
I'm relatively new to Flutter and Cubit pattern and I'm trying to figure out which are the best ways to work with them. Recently my colleague and I have been struggling to reach an agreement where we should inject the cubit and the repositories.
Reading the bloc/cubit documentation, it is not very clear about where we should do it.
IMO, everything that we need to instantiate, should be injected as high in the tree as possible where two different components that will use this information have in common.
For my colleague, each widget can instantiate one cubit, meaning that each widget will have its own instance of the cubit.
I would like to discuss about what are the community thoughts and best practices regarding the dependency injection and architecture regarding cubit.
There is no single answer to that question. It all depends on your project structure and architecture. In general though:
It's OK to create a few cubits/blocs in one screen/widget. Some widgets or screens contain more than one business logic stuff. Cubits are just classes that help you maintain the state, but it's no different than having many animation controllers or text editing controllers, it's just that it serves a more high-level state management. Let's say that you have a comments section in your app. You may have:
a cubit for the comments themselves, to load them, load more on scroll, report error when loading failed etc.
a cubit for each comment that manages the "Like" button under a comment
It's perfectly valid to have it that way.
It's OK to have global cubits for the whole app. There are some things that you need to have access to from the whole application. It usually is navigation (Navigator), and some theme management (Theme), why not something more business-logic related then, like authentication logic, current user context, user's app preferences, etc.? :)
IMO, everything that we need to instantiate, should be injected as high in the tree as possible where two different components that will use this information have in common.
This is a good approach. Most frequently it will be above your routes, so somewhere above your MaterialApp. If you make use of nested Navigators, then this common place could be above this nested Navigator.
On a more technical side, how will you manage the dependencies used in those cubits/blocs is up to you and your liking. I find some of the options:
Instantiating all repositories and other dependencies in main.dart method and then passing them in constructors to your blocs/cubits in Providers.
To reload those dependencies you will need a Hot Restart though, Hot Reload won't be enough.
Putting your dependencies in the widget's tree with Provider, just like blocs/cubits.
Using riverpod instead of provider.
Using a Service Locator pattern with get_it and injectable combo.
The most correct choice will be something that you (and your colleagues) are most comfortable developing with and that scales well.
I'm writing my first Flutter app and struggling with the variety of state management solutions. I decided to start with Provider, but I'm thinking about switching over to BLoC. So far most of the examples I've found are limited to relatively simple things, like showing a list of items or responding to some button presses. In my case, almost all of the app is focused on setting up a rather large hunk of data. (It's basically a whole bunch of forms all working on different bits of a large data structure.)
At the moment, all of the state management is put together into one provider class because most of it is very closely related. For instance, the biggest part of it is a list of items, and then a bunch of subsets of that list. The majority of the data manipulation in the app is on those subsets.
I wasn't really intending to do this in the beginning, but I've found myself putting the code that actually uses the provider pretty close to the top level, and then passing data down the tree. It goes against the whole point of Provider, but it's resulted in less duplicated code. For example, one of the screens in my app has a bunch of cards that all contain very similar lists. The main difference between them is that their contents comes from different lists. So in order to reduce duplicated code, I generalized the cards and lists as much as possible, and passed the necessary data down the tree. I'm also passing down callback functions to handle things like editing an item or removing it from the list.
Does this sound like an okay way to go about it? The fact that I'm passing state and callbacks down the tree feels like a code smell to me, but as I mentioned, it's resulted in less duplicated code. What about the fact that I have a single provider that exposes a lot of stuff? Part of me feels like I should have the data model separated from the provider, and then have multiple smaller providers. If I were to go that route, would BLoC be a better fit than Provider? Would there potentially be a performance boost if I'm able to break up the single provider? Having multiple providers or blocs seems like it could complicate saving changes, but maybe not. Have you ever found that to be an issue?
EDIT: I just found this question, which talks about reusing providers for different data: Flutter Provider. How to have multiple instances of the same provider type?
I hadn't really thought of it like that before, but that's a big part of what I'm struggling with. I have different lists that are modified independently. It's the same widgets that are used in multiple places (sometimes on the same screen), and the functionality in the provider would also be the same if I broke what I have up into smaller pieces. Since Provider is based on type rather than instance, maybe it just isn't going to work in this case.
My first Flutter app used a single Provider class and it worked but was a giant messy class and triggered tons of UI updates.
Currently I use a "models" folder for data models and "services" folder for Provider singletons.
Models has all my data classes with utility and mutator methods. For example a MyCard class to hold values plus MyCard.fromJson() and .toJson() for serialization, and maybe some routines like clear() and validate().
Services is where I create Providers as singletons. For example, a class MyCardService with ChangeNotifier singleton that has a List<MyCard> to hold all the MyCards. If I want a UI widget to update based on changes, like when MyCardService().loadCardsFromNetowrk() completes, I access the service in a build() method using Provider watch or select. If I want to access data outside the build() chain, I access it with the singleton pattern: MyCardService().myVariable. This removes the need for context lookups outside the build() chain.
This provides a lot of control for async access. For example you can have a mutex on get and set methods, a Future<bool> isInitialized that resolves as true or false after your service tries to initialize. You can await this future in any async call that needs to access the service or a FutureBuilder()
I also have some non-Provider service singletons that other services use for stuff like network API access and filesystem caching.
The provider services are mostly organized by UI data consumption. This way a notifyListeners() doesn't trigger UI rebuilds for unmodified widgets (a major problem with my first single-file provider project that resulted is a bunch of individual variables using select instead of one watch).
In the above example, the Provider singleton holds a List and all the methods for working on the List<>. The MyCard data model holds all the methods for working on a MyCard. When I have a widget that edits a single MyCard, I use a stateful widget that does a MyCardService().save(myCard) when editing is finished. This also helps isolate UI tree rebuilds.
Currently am working on Sport Event Scoring app.
The structure of the app goes something like this:
Right now I am trying to include state management using provider package.
Scoring occurs at the very bottom of the Widget tree using a counter and each Widget initializes respectively named model based on a user input on the go - creating Event, adding divisions and players each happens in a dialog.
I know I need to create a provider for list of Events, however, I am not sure how to go about structuring rest of the state management. So far I tried turning each model into a Provider by mixin in the ChangeNotifier and moving all the methods which change the data inside where the state would be managed as well with notifyListeners().
However after a lot of struggling and searching I found that it's not the best approach to manage the state inside models.
What approach would be ideal?
Should I manage the state through the Event List provider, or should I go with creating a provider for each model in a separate file? If so, how?
I am not too sure about the full scope of your project nor how far you are into it but I would highly suggest checking out the BLOC library as an alternative state management solution to simply manage the state in your case.
The learning curve is fairly low with many tutorials on how exactly to use the library alongside the fact that BLOC is much less reliant on listeners from an external persons view using the library from my experiences if that is what you are struggling with.
Here is a link to the BLOC Library
If you are still persistent to use Provider, I would suggest you create a provider for each model in a separate file for multiple reasons such as separation of concerns and ease of management of state and clarity of each different model.
I am using class instances to hold data , within the widget tree I provide a function to render the data to a widget. Rather then calling the function within the widget tree would it be better to make this function a method within the given class?
I think it's not a good practice to mix UI and business logic.
In flutter it's really easy to mess up your code since you write both your business logic and UI code in Dart language. But it depends on you to follow this path, which will definitely make you're application hard to maintain and scale, or try to decouple your business logic from your UI. This makes it really easy to add new features and also debug your application as it scales.
For doing this you can search about different architectures, like Clean architecture, MVC, etc, which would help you not only to decouple your UI from your business logic, but also to decouple your business logic to different layers that would make your code even more maintainble.
I'm working on a prototype for a storefront, and I have a slider where I have the option to see the user's profile. Every user has to register first before they can access the store (is just a prototype).
When a new user registers successfully, they will be immediately sent to the product catalog screen. On the same catalog there will be a slider, and in that slider there will be an option to see the user's profile page.
My question is, how can I send the user's info (name, email and phone number) through the catalog page, then trough the slider, and then to the user's profile page?
I already have a class that holds the user's info that's created on the RegisterPage class/screen.
Unless I'm reading the question wrong, it sounds like some kind of state management solution would be useful here. That is, once you know the user's details, they should be stored in a place that is easily accessible later on from other widgets.
There are a few state management options available:
StatefulWidget - a classic, simplest approach. Have a StatefulWidget which tracks state (user's details), and pass them down the widget tree to a component that can make use of those details. Gets a little unwieldy if you have a deep tree and need to pass the data down through several layers that don't really care about said data.
InheritedWidget. Propagate information down the widget tree and rebuild UI whenever inherited widget itself changes state. Relatively straightforward, simple solution. Unlike StatefulWidget, you can retrieve required data where you need it, and you don't have to pass it down as arguments to yet another widget.
Provider package. A state management solution by Remi Rousselet, endorsed by Google itself. Easy to use and understand, and quite scaleable. Advertised as:
A mixture between dependency injection (DI) and state management, built with widgets for widgets.
Redux. Another state management solution that comes in a form of a package. If you've worked with React/Redux, you will feel right at home. It's not as full-featured as its JavaScript counterpart, but the idea is the same: have a store to which some widgets are listening, and rebuild them whenever data changes.
BLoC / Rx. One of the Google's recommended ways to manage state. Leans heavily on streams. Is somewhat more complex to understand, but rather powerful once you do.
MobX. Another state management solution ported from JavaScript world. I haven't had much experience with it, but I believe it's based on observables, actions and reactions. I expect it should be quite similar to JavaScript's counterpart.
You can read more about each of those by heading over to the dedicated page on the official documentation.
Personally, I'd probably choose something relatively simple first - perhaps InheritedWidget or provider package. However, it heavily depends on your use case and what programming paradigms you're comfortable with / want to experiment with.