Improving Solid widget and context.get<T>() #49
Replies: 2 comments 4 replies
-
Using optional IDs for both providers and signals (in Solid widget)This is wanted by design. You can have providers of the same type unless you specify them in different class SomeClass {
const SomeClass(this.text);
final String text;
}
class Parent extends StatelessWidget {
const Parent({super.key});
@override
Widget build(BuildContext context) {
return Solid(
providers: [
SolidProvider<SomeClass>(
create: () => const SomeClass('1'),
),
],
child: const Child(),
);
}
}
class Child extends StatelessWidget {
const Child({super.key});
@override
Widget build(BuildContext context) {
return Solid(
providers: [
SolidProvider<SomeClass>(
create: () => const SomeClass('2'),
),
],
child: const InnerChild(),
);
}
}
class InnerChild extends StatelessWidget {
const InnerChild({super.key});
@override
Widget build(BuildContext context) {
return Text(context.get<SomeClass>().text);
}
} The following Text widget will render 2 because it finds the first signals: const {
createDesignLanguageSignal,
createThemeModeSignal,
}, This is not something that we're going to implement because it will become exactly like riverpod and we want to avoid that. There is no solution other than the one done by riverpod, but his solution requires global variables. So I think it's better to keep IDs. context.get() is a bit unintuitiveI think it's pretty simple, and Merging providers and signalsThis will increase the complexity because I would have to add |
Beta Was this translation helpful? Give feedback.
-
Using optional IDs for both providers and signals (in Solid widget)
Signal<ThemeMode> createThemeModeSignal() {
const key = 'theme_mode'; // shared preferences key
final themeModeSignal = createSignal<ThemeMode>(
() {
final themeModeString = sharedPreferences.getString(key);
if (themeModeString == null) {
logger.v('No $key key found in shared preferences');
sharedPreferences.setString(key, ThemeMode.system.name);
return ThemeMode.system;
} else {
logger
..v('$key key found in shared preferences with value: $themeModeString')
..v('Valid ThemeMode strings: ${ThemeMode.values.map((ui) => ui.name)}');
if (ThemeMode.values.map((ui) => ui.name).contains(themeModeString)) {
// The value inside shared preferences is valid.
final loadedUi =
ThemeMode.values.singleWhere((ui) => ui.name == themeModeString);
return loadedUi;
} else {
// The value inside shared preferences is not valid.
const resetThemeMode = ThemeMode.system;
sharedPreferences.setString(key, resetThemeMode.name);
return resetThemeMode;
}
}
}(),
);
createEffect((dispose) {
final themeMode = themeModeSignal();
sharedPreferences.setString(key, themeMode.name);
});
return themeModeSignal;
} I am doing so to avoid ending up with long, difficult-to-read Widget build(BuildContext context) => Solid(
signals: {
SignalIds.designLanguage: () => createDesignLanguageSignal(),
SignalIds.themeMode: () => createThemeModeSignal(),
},
child:
// ... After eta reductions it looks like this (now the map can be a compile time constant): Widget build(BuildContext context) => Solid(
signals: const {
SignalIds.designLanguage: createDesignLanguageSignal,
SignalIds.themeMode: createThemeModeSignal,
},
child:
// ... Also, Riverpod requires you to instantiate providers globally (e.g. Thus, i still think optional IDs would be very beneficial. Take for example (note: I know I can't mix map entries with set entries, but it is just to give an idea): signals: {
() => createDesignLanguageSignal(),
() => createThemeModeSignal(),
// only here it makes sense to use IDs
SignalIds.isPoweredOn: () => createPoweredOnSignal(), // type: Signal<bool>
SignalIds.isLoggedIn: () => createLoggedInSignal(), // type: Signal<bool>
}, This way, one can simply call I think that the same should apply to providers as well. After all, DI is being done in either case, so it shouldn't really matter if the object extends context.get() is a bit unintuitive and Merging providers and signalsI see your points. |
Beta Was this translation helpful? Give feedback.
-
At the moment, I think that DI is slightly unintuitive to use. There are some things that I think can be improved.
Using optional IDs for both providers and signals (in Solid widget)
Is there a technical reason why signals require an ID, while providers do not? In my experience:
Solid
are of a type for which there is only one signal. However, I still have to assign it an ID when usingSolid
andcontext.get<SignalBase<T>>
.Regarding this point, I would make IDs optional for both signals and providers.
So that I can write either:
or
or
Of course this syntax doesn't make sense (mixing sets and maps isn't possible), but it is to give an idea. It would be neat to achieve a final solution where an ID does not always have to be passed.
context.get() is a bit unintuitive
I think it is a bit confusing to get a signal by passing an extra ID, otherwise an object that is not a signal will be injected.
I think that
context.get
could be deprecated in favour ofcontext.getProvider
,context.getSignal
,context.getReadSignal
andcontext.getResource
(or maybe even better if we call themcontext.injectProvider
,context.injectSignal
,context.injectReadSignal
andcontext.injectResource
).Merging providers and signals
Do you see a way so that we can merge the signals and providers fields of
Solid
, ending up only with providers, maybe? I don't think this is a necessity, but it might make things a bit simpler.Beta Was this translation helpful? Give feedback.
All reactions