Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please Provides an architectural example of MVC + latest riverpod generator #3119

Open
laterdayi opened this issue Nov 14, 2023 · 15 comments
Open
Assignees
Labels
documentation Improvements or additions to documentation

Comments

@laterdayi
Copy link

laterdayi commented Nov 14, 2023

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

@laterdayi laterdayi added enhancement New feature or request needs triage labels Nov 14, 2023
@laterdayi laterdayi changed the title Provides a riverpod example of the MVC architecture Provides an architectural example of MVC + latest riverpod generator Nov 14, 2023
@laterdayi
Copy link
Author

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

@laterdayi laterdayi changed the title Provides an architectural example of MVC + latest riverpod generator Please Provides an architectural example of MVC + latest riverpod generator Nov 14, 2023
@laterdayi
Copy link
Author

laterdayi commented Nov 14, 2023

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

@laterdayi
Copy link
Author

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?

@rrousselGit
Copy link
Owner

Generally yes. Riverpod doesn't promote putting too much things into a single object

@rrousselGit
Copy link
Owner

To begin with, Riverpod isn't trying to respect MVC

@laterdayi
Copy link
Author

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

@herowws
Copy link

herowws commented Nov 15, 2023

首先,Riverpod 并没有试图尊重 MVC

The design of Riverpod leads to a significant amount of code duplication and a massive workload

@shtse8
Copy link

shtse8 commented Nov 19, 2023

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.

@rrousselGit
Copy link
Owner

You're not supposed to "listen" to notifiers.All listenable state should be placed inside the "state" property

@shtse8
Copy link

shtse8 commented Nov 19, 2023

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?

@TekExplorer
Copy link

TekExplorer commented Dec 2, 2023

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 Futures

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);
}

@hamishjohnson
Copy link

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.

@MannaYang
Copy link

In my project , i use riverpod(v2.4.9) source code like this:

ChatGPT-Flutter-Web

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);
  }
}

@rrousselGit rrousselGit added documentation Improvements or additions to documentation and removed enhancement New feature or request needs triage labels Mar 10, 2024
@login3b7e
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

8 participants