riverpod icon indicating copy to clipboard operation
riverpod copied to clipboard

docs: add more examples and enhance docs with tutorials

Open thisissandipp opened this issue 11 months ago • 13 comments

Describe what scenario you think is uncovered by the existing examples/articles

  • The current set of examples is a great starting point and covers many important use cases, but I think there's still room for more diverse and real-world examples.
  • Additionally, the docs can be enhanced by adding tutorials instead of just linking back to the repository examples.

Describe why existing examples/articles do not cover this case

  • More complex examples like - Firebase login or asynchronous CRUD operations would provide more value to the docs.
  • The existing documentation primarily points to the repository, which might not be as beginner-friendly as structured tutorials. Here are some libraries that have included their examples as a form of tutorials

thisissandipp avatar Jan 15 '25 21:01 thisissandipp

I'd like to get on that, if it's up for grabs šŸ˜€

From what I'm reading in the issue description, this could be achieved with:

  • create tutorial section in the docs
  • add entry for each item in examples (counter, marvel, pub, random_number, stackoverflow, todo)
  • add tutorial for firebase login and asynchronous CRUD and maybe more?

Which tbh I would split into two PR's I guess šŸ¤”

BenAuerDev avatar Jan 19 '25 12:01 BenAuerDev

Current examples are meh.

We need to make new examples, where every one of them demonstrates one specific thing. All current examples are pretty much the same thing, but with a different UI.

One Firebase integration would be cool.

Maybe one for go_router. It sounds commonly requested. Although you'll need my review for that.

And maybe one for shared_prefs or sqlite.

Then an example with a rest API

...

All those could be both in the form of "example" and "tutorial".

rrousselGit avatar Jan 19 '25 12:01 rrousselGit

Don't do everything in one PR. One tutorial or example per PR. And do examples before tutorials, so that we can approve the code before having the English explanation :)

rrousselGit avatar Jan 19 '25 12:01 rrousselGit

Thanks for the feedback and interest, everyone!

@BenAuerDev, since there are multiple tutorials and examples to create, we can split the work. For instance, I’d be happy to work on the Firebase login example and a corresponding tutorial. What do you think about focusing on the REST API example or go_router? We can align patterns together to maintain consistency.

Let me know your thoughts on how we can coordinate - I'm excited to collaborate!

thisissandipp avatar Jan 19 '25 12:01 thisissandipp

Hey @thisissandipp I'd love to collaborate on this 😃 šŸ’Ŗ thanks

Amazing šŸŽ‰ I'll start with REST API then.

After that we still need examples for:

  • go_router
  • shared_prefs or sqlite

maye also serverpod?

And what about the basic examples like counter, timer and todo?

BenAuerDev avatar Jan 19 '25 20:01 BenAuerDev

And what about the basic examples like counter, timer and todo?

I'd delete them.


maye also serverpod?

What would be the added value over the rest API example?

go_router has the added value of showcasing how to convert providers to ValueListenables

rrousselGit avatar Jan 19 '25 23:01 rrousselGit

I'd delete them.

ok :) but don't you think some beginner friendly tutorials might be nice?

What would be the added value over the rest API example?

Sorry I meant serverpod_auth :) I wrote an article about combining it with riverpod and thought it might be a good addition but I guess it's not needed :)

So I'll start with rest API šŸ™Œ

go_router has the added value of showcasing how to convert providers to ValueListenables

I might have some more questions about that once I'll start doing go_router :)

BenAuerDev avatar Jan 20 '25 13:01 BenAuerDev

Hey guys,

I found an example for handling REST Api that is short but (imho) demonstrates it quite well. But I'm keen to hear what you think :)

Example:


import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: UserListScreen(),
    );
  }
}

// Model
class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) => User(
        id: json['id'],
        name: json['name'],
        email: json['email'],
      );
}

// API Repository
class UserRepository {
  final Dio dio;

  UserRepository(this.dio);

  Future<List<User>> fetchUsers() async {
    try {
      final response =
          await dio.get('https://jsonplaceholder.typicode.com/users');
      return (response.data as List)
          .map((userData) => User.fromJson(userData))
          .toList();
    } catch (e) {
      throw Exception('Failed to load users');
    }
  }
}

// Providers
final dioProvider = Provider((ref) => Dio());

final userRepositoryProvider = Provider((ref) {
  return UserRepository(ref.watch(dioProvider));
});

final usersProvider = AsyncNotifierProvider<UsersNotifier, List<User>>(() {
  return UsersNotifier();
});

// Notifier for Users State
class UsersNotifier extends AsyncNotifier<List<User>> {
  @override
  Future<List<User>> build() async {
    final repository = ref.read(userRepositoryProvider);
    return repository.fetchUsers();
  }

  Future<void> refreshUsers() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() async {
      return ref.read(userRepositoryProvider).fetchUsers();
    });
  }
}

// User List Screen
class UserListScreen extends ConsumerWidget {
  const UserListScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final usersAsync = ref.watch(usersProvider);

    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: usersAsync.when(
        data: (users) => RefreshIndicator(
          onRefresh: () => ref.read(usersProvider.notifier).refreshUsers(),
          child: ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index];
              return ListTile(
                title: Text(user.name),
                subtitle: Text(user.email),
              );
            },
          ),
        ),
        error: (error, stack) => Center(child: Text('Error: $error')),
        loading: () => Center(child: CircularProgressIndicator()),
      ),
    );
  }
}

Furthermore I was wondering how to organize the examples like:

  • should the code for each example be in main.dart or be separated in files and dirs? if yes what structure?
  • should we remove all platfrom directories before making PR (e.g. /android, /ios, /linux, /windows, /macos)

Let me know if I forgot something :) that you think we should also take into account or something :)

BenAuerDev avatar Jan 24 '25 10:01 BenAuerDev

should the code for each example be in main.dart or be separated in files and dirs? if yes what structure?

Put it in examples/<yourexample>/

I found an example for handling REST Api that is short but (imho) demonstrates it quite well. But I'm keen to hear what you think :)

Overall I think that's missing a "post" part.

As is, that's similar to what we have on the homepage:

Image

nit:

- ref.read(usersProvider.notifier).refreshUsers(),
+ ref.refresh(usersProvider)
-    final repository = ref.read(userRepositoryProvider);
-    final repository = ref.watch(userRepositoryProvider);
  body: usersAsync.when(

Don't use when. Use switch

rrousselGit avatar Jan 24 '25 13:01 rrousselGit

@rrousselGit Does using switch instead of when sound like a good idea currently? I mean, with when, we ensure all states are handled, but with switch, removing AsyncError(: final error) → Text('error: $error') won't trigger a complaint.

AhmedLSayed9 avatar Jan 24 '25 14:01 AhmedLSayed9

Put it in examples/<yourexample>/

No that I understood :) I just wondered if we should simply have:

examples/<yourexample>/lib/main.dart

or

examples/<yourexample>/lib/main.dart examples/<yourexample>/lib/src/providers.dart examples/<yourexample>/lib/src/UserListScreen.dart

Overall I think that's missing a "post" part.

Noted I'll use an example that uses both GET and POST.

Thanks for the feedback šŸ’Ŗ šŸ™Œ

BenAuerDev avatar Jan 24 '25 14:01 BenAuerDev

If a single file is enough, do that. Fewer files is better. It's easier for folks to navigate in

rrousselGit avatar Jan 25 '25 11:01 rrousselGit

I've just been told "wouldn't it be wonderful if riverpod.dev/docs/tutorials would include full CRUD example, just like the "your first riverpod app"?

Something quite simple, e.g. a TODO app, that involves:

  • a get --> a provider
  • a post --> make it a notifier, show a mutation
  • a put --> show how to use mutation differently
  • a delete --> use that chance to show optimistic updates
  • persist it --> solve #3930 as well

Is that something we can proceed with?

lucavenir avatar Oct 22 '25 20:10 lucavenir