riverpod
riverpod copied to clipboard
Please Provides an architectural example of MVC + latest riverpod generator
Is your feature request related to a problem? Please describe. As a powerful riverpod, I hope the official can provide MVC + the latest riverpod generator architecture example
Describe the solution you'd like As a powerful riverpod, I hope the official can provide MVC + the latest riverpod generator architecture example
For example: I have A page, A controller, in A controller store a lot of data and update data methods (string Boolean model class, update string update model class to get data), such a structure with the latest syntax generator should be how to achieve, Could the official provide a similar example in the document
@rrousselGit
a.controller file
class AController extends GetxController {
// -------------------------------------------------------------------------------------------- > State & Controller
static EngineerRepairController get to => Get.find();
CallDetailModel callDetailData = CallDetailModel();
List<FaultTypeListModel> faultTypeList = [];
@override
void onInit() async {
super.onInit();
handleGetCallDetail();
}
// -------------------------------------------------------------------------------------------- > Updater
void updateCallDetail(CallDetailModel val) => callDetailData = val;
void updateFaultType(List<FaultTypeListModel> val) => faultTypeList = val;
// -------------------------------------------------------------------------------------------- > Action
Future handleGetCallDetail() async {
try {
Map<String, dynamic> params = {'id': Get.arguments['id']};
dynamic res = await HttpService.to.get(EngineerRepairApis.getCallDetailDataApi, params: params);
updateCallDetail(CallDetailModel.fromJson(res));
update();
} catch (e) {
}
}}
This is how I define and update data in getx. Now I'm trying to switch to riverpod. How do I implement this in the latest riverpod generator
You may be looking for https://riverpod.dev/docs/essentials/side_effects / https://github.com/rrousselGit/riverpod/blob/master/examples/todos/lib/todo.dart
This example looks like it only maintains todo data, and I have a lot of data in one controller, todolist, movieDetailData..... Do you want to create a provider for each data?
Generally yes. Riverpod doesn't promote putting too much things into a single object
To begin with, Riverpod isn't trying to respect MVC
How is this different from storing a lot of data in controller in getx? Is there a performance gap? Can you describe it in the documentation
首先,Riverpod 并没有试图尊重 MVC
The design of Riverpod leads to a significant amount of code duplication and a massive workload
I've reviewed the documentation multiple times but still have questions. Specifically, I'm unsure about monitoring state changes while accessing the notifier in a widget. The notifier offers many useful methods and properties, unlike the state. Also, I'm uncertain about the appropriate placement for utility objects such as streams, custom utilities, and caches. Should these be included in the notifier? Furthermore, I'm unclear about the optimal time for initializing these utilities. Would it be during the constructor or the build method? Additionally, I'm concerned about potential issues if the notifier is rebuilt.
You're not supposed to "listen" to notifiers.All listenable state should be placed inside the "state" property
Thank you for your response. I'm still a bit unclear, though. Are you suggesting that all utilities, since they're not listenable, should be placed in the notifier? And for listenable utilities, should they be included in the state?
Additionally, I'm not actively listening to notifiers. In a relational model, we often need to access objects from other providers. I utilize conventional getters to fetch objects by ID from other providers, avoiding repetition. This is one reason I need to access the notifier while also listening to the state.
Could you please provide more insight on this, particularly regarding the optimal placement of utilities like streams, custom utilities, and caches? Should these be initialized in the constructor or the build method, and what are the implications if the notifier is rebuilt?
all state in riverpod should be created and initialized in build (for notifiers) or the top-level function (in functional providers)
avoid storing any other state in your notifiers.
riverpod handles streams for you. simply set the return type to Stream<Foo>
and you'll get an AsyncValue
the same as Future
s
If you want to get an object by it's id, add a parameter to build(int id)
or function foo(FooRef ref, int id)
which will create a family provider accessed with ref.watch(fooProvider(4));
this can be like:
@riverpod
List<Todo> todos(TodosRef ref) => []; // a list of todos
@riverpod
Todo todo(TodoRef ref, int id) { // a specific todo
final todos = ref.watch(todosProvider);
return todos.firstWhere((todo) => todo.id == id);
}
Having used riverpod + flutter_hooks since riverpod came out, you don't really need to do MVC. I use flutter_hooks for ephemeral state and riverpod for dependency injection + global state and it works a charm.
In my project , i use riverpod(v2.4.9) source code like this:
The code call path : UI -> Provider -> Repository -> Http If the request is completed, the results are returned in order, you can control each stage, and handle state in Provider
/// Widget build() method
final conversations = ref.watch(chatConversationsProvider);
chatConversationsProvider.dart
@riverpod
class ChatConversations extends _$ChatConversations {
///
/// Conversation List , Like Select\Insert\Update\Delete
///
@override
(int, String, List<ChatConversationInfo>?) build() =>
(StatusProvider.stateDefault, '', null);
void selectConversationList() async {
var result = await ref
.read(chatRepositoryProvider.notifier)
.selectConversationList();
Logger().d("selectConversationList - $result");
state = (result.code, result.msg, result.data ?? <ChatConversationInfo>[]);
}
}
chatRepositoryProvider.dart
@Riverpod(keepAlive: true)
class ChatRepository extends _$ChatRepository {
@override
void build() {}
///
/// Get conversation list
///
Future<BaseResult<List<ChatConversationInfo>>>
selectConversationList() async {
var uri = Uri.http(ApiProvider.backendUrl, 'api/llm/v1/conversation/list');
try {
final response = await ref.read(httpRequestProvider.notifier).get(uri);
var json = jsonDecode(utf8.decode(response.bodyBytes));
var data = BaseResult<List<ChatConversationInfo>>.fromJson(
json,
(jsonData) => (jsonData as List<dynamic>)
.map((item) => ChatConversationInfo.fromJson(item))
.toList());
return data;
} catch (e) {
return BaseResult(
code: -1, msg: e.toString(), success: false, data: null);
}
}
}
httpRequestProvider.dart
@Riverpod(keepAlive: true)
class HttpRequest extends _$HttpRequest {
@override
void build() {}
///
/// http - get
///
Future<Response> get(
Uri url, {
Map<String, String>? headers,
int timeout = _reqTime,
}) {
Logger().d("HttpProvider - Request = $url");
var response =
http.get(url, headers: _tokenHeader()).timeout(_timeout(timeout));
return _handleResponse(response);
}
}
Is above example by @MannaYang a recommended pattern for the current state of Riverpod?
I've used 1.x versions and am used to creating a provider that passes in a Ref to a repository class for example, which then uses the ref to access a (REST API) client exposed through another provider; this seems very similar.
Is it still recommended to work like this? It's basically as in above chatRepositoryProvider.dart
and httpRequestProvider.dart
which have a void state.
If not the recommended pattern, what is the suggested approach? E.g. have an annotated provider for all methods such as selectConversationList()
for example, which would generate a FutureProvider?
Thanks in advance, trying to wrap my head around the changes 1.x to 2.x + code generation.
do not have a void provider. that is completely pointless. instead, have an actual normal repository class, and provide that in a functional provider
Class provider methods should mutate the state. otherwise, why have it?