Skip to content

Instantly share code, notes, and snippets.

@hectorAguero
Last active May 10, 2024 17:05
Show Gist options
  • Save hectorAguero/c71c5a088421479e1525e450353f7e28 to your computer and use it in GitHub Desktop.
Save hectorAguero/c71c5a088421479e1525e450353f7e28 to your computer and use it in GitHub Desktop.
AsyncValue with ValueNotifier
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
typedef AsyncValueNotifier<T> = ValueNotifier<AsyncValue<T>>;
class _HomePageState extends State<HomePage> {
final AsyncValueNotifier<User> userNotifier = ValueNotifier(AsyncLoading());
@override
void initState() {
super.initState();
simulateFetchUser();
}
Future<void> simulateFetchUser() async {
try {
// Simulating network request
await Future.delayed(const Duration(seconds: 1));
const user = User(name: 'John Doe', id: 1, phones: ['123-456-7890']);
userNotifier.value = AsyncData(user);
//throw Exception('Failed to fetch user');
} catch (e) {
userNotifier.value = AsyncError(e as Exception);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sealed Classes with Switch')),
body: Center(
child: ValueListenableBuilder<AsyncValue<User>>(
valueListenable: userNotifier,
builder: (_, value, __) => switch (value) {
AsyncData(:final data) =>
Text('${data.name}\n${data.phones ?? 'No Phones'}'),
AsyncError(:final exception) => Text('$exception'),
AsyncLoading() => const CircularProgressIndicator(),
},
),
),
floatingActionButton: UserFab(userNotifier: userNotifier),
);
}
}
class UserFab extends StatelessWidget {
const UserFab({required this.userNotifier, super.key});
final AsyncValueNotifier<User> userNotifier;
@override
Widget build(BuildContext context) {
return FloatingActionButton.extended(
onPressed: () {
final user = userNotifier.value.dataOrNull;
if (user == null) return;
userNotifier.value = AsyncData(
user.copyWith(phones: user.phones == null ? ['987-654-3210'] : null),
);
},
label: const Text('Change Phones'),
icon: const Icon(Icons.refresh),
);
}
}
sealed class AsyncValue<T> {
T? get dataOrNull => null;
}
class AsyncData<T> extends AsyncValue<T> {
final T data;
AsyncData(this.data);
@override
T get dataOrNull => data;
}
class AsyncError<T> extends AsyncValue<T> {
final Exception exception;
AsyncError(this.exception);
}
class AsyncLoading<T> extends AsyncValue<T> {}
// User immutable class
class User {
final String name;
final int id;
final List<String>? phones;
const User({required this.name, required this.id, this.phones});
@override
String toString() {
return 'User{name: $name, id: $id, phones: $phones}';
}
@override
bool operator ==(covariant User other) {
if (identical(this, other)) return true;
return other.name == name &&
other.id == id &&
listEquals(other.phones, phones);
}
@override
int get hashCode => name.hashCode ^ id.hashCode ^ phones.hashCode;
static const _unset = Object();
User copyWith({
String? name,
int? id,
Object? phones = _unset, // default to special marker
}) {
return User(
name: name ?? this.name,
id: id ?? this.id,
phones: phones == _unset ? this.phones : phones as List<String>?,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment