ServicesBinding.defaultBinaryMessenger problem while using Flutter's compute function - flutter

i'm building an explorer using flutter getting apps length will take a while so i tried to get the value using annother Isolate, that's part of my main.dart code
Future<int> getValueFromIsolate() async {
return await compute(
apps,
0,
);
}
Future<int> apps(int n) async {
int value = 0;
List apps = await DeviceApps.getInstalledApplications(
includeAppIcons: true,
includeSystemApps: true,
onlyAppsWithLaunchIntent: true,
);
print(apps.length);
value = apps.length;
print(value);
return value;
}
And so there is my main function
void main() {
WidgetsFlutterBinding.ensureInitialized();
getValueFromIsolate().then(
(value) {
print("Then the value is $value");
},
);
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AppProvider(),
),
ChangeNotifierProvider(
create: (_) => CategoryProvider(),
),
ChangeNotifierProvider(
create: (_) => CoreProvider(),
),
],
child: MyApp(),
),
);
}
But i still got this error
I/flutter ( 6234): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
I/flutter ( 6234): If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first.
I/flutter ( 6234): If you're running a test, you can call the TestWidgetsFlutterBinding.ensureInitialized() as the first line in your test's main() method to initialize the binding.
I don't understand what it's happening
and i don't know what to do..!!
Please need your help Thanks for reading and your help

Currently Flutter platform channel has limitation - communications are only supported by the main isolate which created when your application launched.
DeviceApps.getInstalledApplications does invokes platform-channel to access platform API and
you invoke it from isolate - compute() creates new one for you
However, there are two plugins to help you out:
flutter_isolate provides a
replacement isolate that can communicate to plugins because it creates
its own UI backing (nothing that you see or have to deal with, just
technically),
isolate_handler The
advantage in using this package rather than flutter_isolate itself is
that this adds handling capabilities, you can start several isolates,
keep track of them and you don't have to set up your own communication
between the isolate and the main thread (something you have to do
manually with both the original stock Isolate and FlutterIsolate)
because it's abstracted away and readily available.
If you still have quesdtion why? or interested to deep dive - read all discussion with Flutter developers in github issue

Thanks again Isolate Handler solve my problem

Related

How to migrate Provider to Riverpod?

I got this error:
'ChangeNotifierProvider' isn't a function. Try correcting the name to
match an existing function, or define a method or function named
'ChangeNotifierProvider'
I'm looking to migrate provider to Riverpod, but it is difficult for me to do that
This is my code:
void main() async {
await Hive.initFlutter();
await Hive.openBox("Habit_Database");
runApp(MultiProvider(providers: [
ChangeNotifierProvider(
create: (context) => UserProvider(),
),
], child: const MyApp()));
}
How can I solve this issue?
Thanks for any help you can provide
I'm working on this right now for a client. I have chosen to maintain the existing provider infrastructure while slowly migrating one provider at a time to riverpod.
To make this work, I first constructed a renaming shim for all of the exported provider functions. It imports provider, and establishes typedefs to alias those names to universally end in "X", including the extension on BlockContext which becomes .readX and .selectX. I tested this by not having the "X" initially, then renaming each symbol in VSC one at a time, which surprisingly worked well. The renaming shim looks something like:
import 'package:provider/provider.dart' as provider;
typedef ProviderX<T> = provider.Provider<T>;
typedef MultiProviderX = provider.MultiProvider;
typedef ChangeNotifierProviderX<T extends ChangeNotifier> = provider.ChangeNotifierProvider<T>;
which continues for about 100 lines. The tricky ones are the extensions:
extension ReadContext on BuildContext {
T readX<T>() => provider.ReadContext(this).read<T>();
}
extension SelectContext on BuildContext {
R selectX<T, R>(R Function(T value) selector) => provider.SelectContext(this).select(selector);
}
Admittedly, once I started the pattern, Github copilot eagerly offered me line after line, and was wrong for only a few things initially.
Next, I added the RiverPod ProviderScope to my runApp, and selected a particular provider to migrate. I created the equivalent in RiverPod, nicely namespaced because "FooProvider" became "fooProvider", and then located all references to that class in .readX or ConsumerX access. I inserted the equivalent with Consumer blocks or ConsumerWidget widgets, giving me a ref to use with ref.read or ref.watch as appropriate.
It's not trivial. But once you get over "the great rename" hurdle, the rest is just a rather mechanical translation, and can be done incrementally.
The error message is indicating that ChangeNotifierProvider isn't a function. You are trying to migrate from provider to Riverpod.
In Riverpod, the equivalent class for ChangeNotifierProvider is ChangeNotifierProvider.autoDispose.
So you should replace this line:
ChangeNotifierProvider(
create: (context) =>
UserProvider(),
),
with this line:
ChangeNotifierProvider.autoDispose( create: (context) => UserProvider(),),
This should solve the issue.

ProxyProvider2 in Riverpod (the provider must be created on startup)

I am using the game template of Flutter casual game.
Flutter casual game template: Link
They are using a provider actually. They have this:
ProxyProvider2<SettingsController, ValueNotifier<AppLifecycleState>,
AudioController>(
// Ensures that the AudioController is created on startup,
// and not "only when it's needed", as is default behavior.
// This way, music starts immediately.
lazy: false,
create: (context) => AudioController()..initialize(),
update: (context, settings, lifecycleNotifier, audio) {
if (audio == null) throw ArgumentError.notNull();
audio.attachSettings(settings);
audio.attachLifecycleNotifier(lifecycleNotifier);
return audio;
},
dispose: (context, audio) => audio.dispose(),
),
As you can see in the comment of code, it says
`Ensures that the AudioController is Created on startup',
and the ProxyProvider has the dispose parameter and it already pass the audio.dispose()
Therefore, I actually convert this into riverpod:
/// The AudioControllerProvider is used to control the audio of the game.
final audioControllerProvider = Provider.autoDispose<AudioController>((ref) {
// final appLifeCycle = ref.watch(appLifeCycleStateProvider);
final audioController = AudioController();
audioController.initialize();
final settingsController = ref.watch(settingsControllerProvider);
final appLifeCycle = ref.watch(appLifecycleStateProvider);
audioController.attachSettings(settingsController);
audioController.attachLifecycleNotifier(appLifeCycle);
ref.onDispose(audioController.dispose);
return audioController;
});
If we are trying to observe, as you what can see, the provider is auto dispose, and I called the
ref.onDispose()
since the old one has the dispose parameter.
Then since this audio controller must be created on start up. This is what I did, by creating a ProviderContainer before the runApp() function
// Run Some Riverpod Providers during startup
final container = ProviderContainer();
container.read(audioControllerProvider);
runApp(
UncontrolledProviderScope(
container: container,
child: MyApp(
adsController: adsController,
gamesServicesController: gamesServicesController,
),
),
);
So what actually the problems there?
It actually working fine at all, the problem there is the dispose. As you can see the audiocontroller was called on startup. But when I go to the new screen by calling this function
GoRouter.of(context).push(
'/game/${questGame[index].gameCategory.gameCategoryKey}');
}),
Then the audio controller it will triggered the dispose. I think the audio controller auto dispose must be not triggered since the provider was called in the main method, right?

Flutter: accessing providers from other providers

For my flutter project, I am using the following multiple providers below:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<FirstProvider>(
create: (context) => FirstProvider(),
),
ChangeNotifierProvider<SecondProvider>(
create: (context) => SecondProvider(),
),
ChangeNotifierProvider<ThirdProvider>(
create: (context) => ThirdProvider(),
),
ChangeNotifierProvider<FourthProvider>(
create: (context) => FourthProvider(),
),
],
child: const MainApp(),
);
}
Because sometimes I need to either get data or call functions from different providers from another provider, I am using it like this:
//First Provider
class FirstProvider with ChangeNotifier {
void callFunctionFromSecondProvider({
required BuildContext context,
}) {
//Access the SecondProvider
final secondProvider= Provider.of<SecondProvider>(
context,
listen: false,
);
secondProvider.myFunction();
}
}
//Second Provider
class SecondProvider with ChangeNotifier {
bool _currentValue = true;
void myFunction(){
//Do something
}
}
The callFunctionFromSecondProvider()of the FirstProvider is called from a widget and it will call myFunction() successfully, most of times.
Depending on the complexity of the function, I am sometimes experiencing that I can't access the SecondProvider, presumably due to context being null, when the widget state changes.
I am reading some documents online regarding provider, and they are suggesting changenotifierproxyprovider for what I understood as 1 to 1 provider relationship.
However, in my case, one provider needs to be accessed by multiple providers and vice versa.
Question:
Is there a more appropriate way that I can approach my case where one provider can be accessed by multiple providers?
EDIT:
Accessing provider should also be able to access different variable values without creating a new instance.
Instead of passing context to the callFunctionFromSecondProvider function add the second provider as the parameter. So the function looks like the below.
Not sure this is the correct way of doing that but my context null issue was fixed this way.
void callFunctionFromSecondProvider({
required SecondProvider secondProvider,
}) {
secondProvider.myFunction();
}
}
Alright.
So it looks like Riverpod by the same author is the way to go as it addresses alot of flaws such as Provider being dependent on the widget tree, in my case, where the underlying issue came from.
—--------
For the time being, I still need to use the provider and for a quick and dirty solution, I am providing the context of not only the current widget that I am trying to access the provider, but also passing the parent context of the widget directly, so that in case a modal (for example) is closed, then any subsequent provider call can still be executed using the parent context.
Hope this helps.

ChangeNotifierProxyProvider not initiated on build

I'm trying to understand multiproviders in Flutter. In my App, one Provider need to change based on a value from an other Provider.
AuthProvider gets initiated higher up in the widget tree on build. Works like a charm with automatic sign in if possible...
In a lower placed widget, I try to initiate two other Providers. One, WhatEver, is not depended on other data and gets initiated on build like it is supposed to using ChangeNotifierProvider.
ProductList however is depended on AuthProvider. If log in status is changed, the ProducList should update accordingly.
In my attempts, I've found out, ie found on SO, that ChangeNotifierProxyProvider is the right way to go. But when I run the App, it seems like the 'create'-part of ChangeNotifierProxyProvider is not initiated when the widget gets build. It seems like the ProductList provider is not initiated until it's read or written to.
What have I misunderstood using MultiProviders and ChangeNotifierProxyProvider?
return MultiProvider(
providers: [
ChangeNotifierProvider<WhatEver>(create: (context) => WhatEver()),
ChangeNotifierProxyProvider<AuthProvider, ProductList>(
create: (_) => ProductList(Provider.of<AuthProvider>(context, listen: false)),
update: (_, auth, productList) => productList..reloadList(auth)
),
],
The ProductList looks like this:
final AuthProvider _authProvider;
static const String _TAG = "Shop - product_list.dart : ";
ProductList(this._authProvider) {
print(_TAG + "ProductList Provider initiated");
reloadList(this._authProvider);
}
void reloadList(AuthProvider authProvider) {
print(_TAG + "ProductList reload started");
if (authProvider.user==null) {
print(_TAG + "ProductList: _authProvider == null");
_loadBuiltInList();
} else {
print(_TAG + "ProductList: user = " + authProvider.user.displayName);
_loadFirestoreList();
}
}
I have code that does this:
ChangeNotifierProxyProvider<AuthService, ProfileService>(
create: (ctx) => ProfileService(),
update: (ctx, authService, profileService) =>
profileService..updateAuth(authService),
),
My ProfileService() does not rely on AuthService being available when it is constructed. The code works fine :)
The ChangeNotifierProxyProvider documentation explicitly describes this approach:
Notice how MyChangeNotifier doesn't receive MyModel in its constructor
anymore. It is now passed through a custom setter/method instead.

How can I dispose ChangeNotifierProvider<T>.value( ) in Flutter

how can I dispose ChangeNotifierProvider.value( ) in Flutter
so there will be no memory leaks
I made the object that is as a value to the ChangeNotifierProvider.value( ) as a singleton
cause I need more than page to share their state in one object
ChangeNotifierProvider automatically calls the dispose() method of the state when it is needed. You need not do anything about that. They talk about this in this video:Pragmatic State Management in Flutter (Google I/O'19)
You need to use the dispose() method of State or the default constructor of ChangeNotifierProvider. The latter automatically disposes of the object created in the create function.
I wonder why you are using ChangeNotifierProvider.value() instead of ChangeNotifierProvider(), but assuming you want to pass the value to a different page, you can combine both as follows.
ChangeNotifierProvider<Foo>(
create: (context) => Foo(), // this Foo object is disposed of automatically
)
Somewhere down the tree:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context2) => Provider<Foo>.value(
value: Provider.of<Foo>(context, listen: false),
child: NextPage(),
),
),
)
Note: You won't have to do this if you provide the value from above MaterialApp.