Skip to content

Instantly share code, notes, and snippets.

@Zekfad
Created December 30, 2022 17:01
Show Gist options
  • Save Zekfad/b54462f0563ceb81ac6697c24bd79a41 to your computer and use it in GitHub Desktop.
Save Zekfad/b54462f0563ceb81ac6697c24bd79a41 to your computer and use it in GitHub Desktop.
Riverpod regression example

Riverpod regression example

Created with <3 with dartpad.dev.

import 'dart:math';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:core';
typedef FilterNotifierBuilder<T, Arg> = FutureOr<T> Function(AutoDisposeAsyncNotifierProviderRef<T> ref, Arg arg);
class FilterNotifier<T, Arg> extends AutoDisposeFamilyAsyncNotifier<T, Arg> {
FilterNotifier(this.builder);
final FilterNotifierBuilder<T, Arg> builder;
@override
FutureOr<T> build(Arg arg) => builder(ref, arg);
@override
bool updateShouldNotify(AsyncValue<T> previous, AsyncValue<T> next) {
if (!isLocked && previous != next) {
if (next.isRefreshing) // manual refresh
return true;
if (previous.isLoading && !next.isLoading) // finish loading
return true;
if (previous.valueOrNull == next.valueOrNull) // discard updates of the same value
return false; // skip loading if it's not a manual refresh
return true;
}
return false;
}
bool get isLocked => locks > 0;
int locks = 0;
Future<void> lock(Future<void> Function() executor) async {
final beforeLockState = state;
state = AsyncLoading<T>().copyWithPrevious(beforeLockState);
locks++;
await executor();
locks--;
if (!isLocked) // no more locks, we can request actual data
ref.invalidateSelf(); // emits loading and then data from build
}
static AutoDisposeAsyncNotifierProviderFamily<FilterNotifier<T, Arg>, T, Arg> createProvider<T, Arg>(
FilterNotifierBuilder<T, Arg> builder,
) =>
AsyncNotifierProvider.autoDispose.family(
() => FilterNotifier<T, Arg>(builder),
);
}
class Test {
Test(this.i);
final int i;
@override
String toString() => '$i';
}
class TestNotifier<T, Arg> extends AutoDisposeFamilyAsyncNotifier<T, Arg> {
TestNotifier({
required this.builder,
});
final FilterNotifierBuilder<T, Arg> builder;
@override
FutureOr<T> build(Arg arg) => builder(ref, arg);
void replaceState(T newState) async {
state = AsyncValue<T>.data(newState).copyWithPrevious(state);
}
Future<void> replaceStateAsync(T newState) async {
return Future.microtask(() => replaceStateAsync(newState));
}
}
final testNotifierProvider = AsyncNotifierProvider.autoDispose.family<TestNotifier<Test, int>, Test, int>(
() => TestNotifier(builder: (ref, arg) async =>
Future.microtask(() => Test(arg)),
),
);
/// If use without proxy provider, wont fail
final proxyProvider = FilterNotifier.createProvider<int?, int>(
(ref, id) async =>
(await ref.watch(testNotifierProvider(id).future)).i,
);
final testProvider = FilterNotifier.createProvider<int, int>(
(ref, id) async =>
(await ref.watch(proxyProvider(id).future))! + 2,
);
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
Consumer(
builder: (context, ref, child) {
return ref.watch(testProvider(1)).when(
data: (data) => Text('Data: $data'),
loading: () => const Text('Loading'),
error: (error, stackTrace) {
print('Error');
print(error);
print(stackTrace);
return const Text('Error');
},
);
},
),
Consumer(
builder: (context, ref, child) =>
ElevatedButton(
child: const Text('Replace state'),
onPressed: () async => {
ref.read(testProvider(1).notifier).lock(
() async {
// await Future<void>.delayed(const Duration(seconds: 1));
ref.read(testNotifierProvider(1).notifier).replaceState(Test(Random().nextInt(100)));
}
),
},
),
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment