Is there a way to get context inside getters? - flutter

I'm using flutter_localizations to internationalize my app. So i have this class
final Article article;
ArticleViewModel({required this.article});
String get name {
return article.name;
}
String get isGood {
return article.isGoods ? 'Goods' : 'Services';
}
}
Inside the isGood getter, i need to return "Goods" or "Services" translated depending on the language
but i don't have the context to call Applocalization.of(context)
I need a good approach to achieve this wihthout passing a BuildContext as parameter.

You can try my approach, I have used this way for many of my apps. My way is inject BuildContext to a singleton class with get_it, like this:
#singleton
class AppContext {
final navigatorKey = GlobalKey<NavigatorState>();
BuildContext get navigatorContext => navigatorKey.currentState!.context;
}
then at your first time your app run, put the injected navigatorKey to MaterialApp:
MaterialApp(
navigatorKey: getIt<AppContext>().navigatorKey,
)
Then, you can get BuildContext after that everywhere with:
getIt<AppContext>().navigatorContext

You could pass a BuildContext to your ViewModel (like you did with article) and call Applocalization.of(context) inside your getter

Related

How to access flutter localization without a context

I am using flutter localization from their official documentation here, and I am using clean architecture in my project. I want to access the app localization class without a context so I can translate the error messages in the repository file.
Here is an example:
class UserRepository{
Future<Either<Failure, Unit>> logOut() async{
try{
return const Right(unit);
}catch(e){
return const Left(AuthFailure('I want to translate this error based on app`s language'));
}
}
}
Well, since this is not the best practice to do, you can create a custom localizations widget that gets you the Localizations.of(context) but here the context will be obtained by a GlobalKey that you to get from a widget that we can access the Localization widget context with it, which is inside the MaterialApp of course.
For this, you can use the navigatorKey to achieve it:
// somewhere else in your project
GlobalKey<NavigatorState> ourNavigatorKey = GlobalKey<NavigatorState>();
// main.dart
MaterialApp(
navigatorKey: ourNavigatorKey,
//...
Now that you assigned that key, you can create a WithoutContextLocalizations widget:
Localizations withoutContextLocalizations() {
final BuildContext context = ourNavigatorKey.currentContext!;
return Localizations.of<Localizations>(context, Localizations)!;
}
Now from any place instead of using :
Localizations.of(context);
You can use :
WithoutContextLocalizations();

Best practice for passing param Riverpod's providers only once

I just want to build a Provider which asks params only one and inits correctly.
Since I am just passing params only once, I don't prefer to use .family methods.
I prefer to use .autoDispose which considered the better way.
Here my tryouts:
I tried to make my own .init() method. But it's disposing as soon as method called if it's .autodispose() and the widget not started to listen my provider yet (that's expected). Therefore I couldn't consider a safe way to do that.
I tried .overrideWith() method in a widget basis. But it's neither worked nor I am sure that it's best practice.
Here is my simple code:
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
final myString = 'Hey';
#override
Widget build(BuildContext context, WidgetRef ref) {
//Not worked
ProviderContainer(
overrides: [messageProvider.overrideWith(() => ViewModel(myString))]);
return Scaffold(
body: ProviderScope(
//Not worked either
overrides: [messageProvider.overrideWith(() => ViewModel(myString))],
child: Center(
//I just didn't use .when to shorter code
child: Text(ref.watch(messageProvider).value!.counter.toString()),
),
),
);
}
}
final messageProvider = AsyncNotifierProvider.autoDispose<ViewModel, Model>(
() => throw UnimplementedError());
class ViewModel extends AutoDisposeAsyncNotifier<Model> {
final String param;
ViewModel(this.param);
#override
FutureOr<Model> build() {
//Make some fetch with param, (only once!)
return Model(param.length);
}
}
When I run that. It gives UnimplementedError
Waiting your suggestions & fixes. Thanks in advance!
Expected:
Works properly.
#riverpod
ViewModel myViewModel(MyViewModelRef ref, String param){
return ViewModel(param);
}
This is autoDispose by default in Riverpod 2. If you don't want to auto dispose you can use #Riverpod(keepalive:true) instead of #riverpod
If you don't want to pass the param to the provider, you can eliminate it and hardcode the value to the ViewModel, but at that point, if there are no other dependencies, might as well make it a public final variable in some file, since it looks like this is a singleton that never changes so it is questionable what you'd achieve by making it a Riverpod provider.

Modifying Lists inside of classes - Flutter, Riverpod, Hooks

I would like to have a class that contains a list of another class and be able to update it using Riverpod/Hooks. I'm trying to figure out the best syntax or even way to approach the issue.
I'm starting with a StateProvider to make it simpler and thought to use the List method .add() to add to the list but I'm getting the error: "The method 'add' can't be unconditionally invoked because the receiver can be 'null'." or null check operator used on null value (when I add the !)- so I think I'm not writing it correctly and I'm sure it has to do with the Riverpod or even Dart syntax.
Any suggestions or resources would be appreciated. Thanks in advance.
So for example:
Session({this.title, this.sessionThings})
String? title;
List<SessionThings> sessionThings;
}
class SessionThings{
SessionThings({this.title, this.time})
String? title;
double? time;
}
ref.read(buildExSessionProvider.notifier).state = Session()..sessionThings.add(SessionThings(title: 'something', time: 20);
using:
hooks_riverpod: ^1.0.3
flutter_hooks: ^0.18.3
Do not access state outside a notifier. Something like this could work:
class SessionStateNotifier extends StateNotifier<Session?> {
SessionStateNotifier() : super(null);
void create(String title) => state = Session(title: title);
void add(SessionThings things) =>
state = Session(title: state?.title, sessionThings: [...state?.sessionThings ?? [], things]);
}
Use with hooks
class MyApp extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final state = useState(0);
final Session? session = ref.watch(sessionProvider);
final SessionStateNotifier sessionStateNotifier = ref.watch(sessionProvider.notifier);
return <widget>
}
}
``

I really need help to understand the nature of Static method

When a instance function being declare Static, does that mean that it can't return a new instance of another object?
Aren't the purpose of the Static keyword is to save the memory, does that mean when i call the static member, it had to return for me the EXACT object ?
class Provider extends InheritedWidget {
final bloc = Bloc();
bool updateShouldNotify(_) => true;
Provider({Key key, Widget child}) : super(key: key, child: child);
static Bloc of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<Provider>().bloc;
}
}
class SignUp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// This is not the same object
final bloc = Provider.of<Bloc>(context);
}
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
// This is not the same object
final bloc = Provider.of<Bloc>(context);
}
Once function is declared static, that means it is no longer "instance" function, but rather class function.
Declaring function static means that you no longer have access to instance (i.e. this). This is just function aliased by the class it is declared in.
Static function can return anything, including new instances of classes, where it is declared.
Static function will not help you to save memory.
My assumption though (I might be wrong) is that static function is a little bit easier for compiler to compile as it does not take part in polymorphism.
Everything from Alex above is correct.
I just want to point the most important differences you should no:
It is not related to any object, and so it can be called directly from the class.
In Java and maybe other languages polymorphic rules no longer work as expected. For this many people do use the Singleton-Pattern, which itself is based on a static function.
Further reading:
https://en.wikipedia.org/wiki/Static_(keyword)
https://en.wikipedia.org/wiki/Singleton_pattern
https://www.javatpoint.com/static-keyword-in-java
https://www.geeksforgeeks.org/static-methods-vs-instance-methods-java/
In general you should try doing some own research before asking questions here, and so make questions less random ;)

How to create a dependency for ChangeNotifierProvider and make it wait to complete?

I have ChangeNotifierProvider object that uses data stored sqflite asset database which need to be loaded at the beginning as future. The problem is that ChangeNotifierProvider doesn't wait for future operation to complete. I tried to add a mechanism to make ChangeNotifierProvider wait but couldn't succeed. (tried FutureBuilder, FutureProvider, using all together etc...)
Note : FutureProvider solves waiting problem but it doesn't listen the object as ChangeNotifierProvider does. When I use them in multiprovider I had two different object instances...
All solutions that I found in StackOverflow or other sites don't give a general solution or approach for this particular problem. (or I couldn't find) I believe there must be a very basic solution/approach and decided to ask for your help. How can I implement a future to this code or how can I make ChangeNotifierProvider wait for future?
Here is my summary code;
class DataSource with ChangeNotifier {
int _myId;
List _myList;
int get myId => _myId;
List get myList => _myList;
void setMyId(int changeMyId) {
_myId = changeMyId;
notifyListeners();
}
.... same setter code for myList object.
DataSource(){initDatabase();}
Future<bool> initDatabase() {
.... fetching data from asset database. (this code works properly)
return true;
}
}
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<DataSource>(
create: (context) => DataSource(),
child: MaterialApp(
home: HomePage(),
),
);
}
}
Following code and widgets has this code part (it works fine)
return Consumer<DataSource>(
builder: (context, myDataSource, child) {.......
There are multiple ways that you can achieve. The main point of it is that you should stick to reactive principle rather than trying to await the change. Say for example, you could change the state of boolean value inside the DataSource class when the ajax request changes
class DataSource extends ChangeNotifier{
bool isDone = false;
Future<bool> initDatabase(){
//Do Whatever
isDone = true;
notifyListeners();
}
}
Then you could listen to this change in the build method like so
Widget build(BuildContext ctx){
bool isDone = Provider.of<DataSource>(context).isDone;
if(isDone){
// render result
}else{
// maybe render loading
}
}