You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

130 lines
4.2 KiB

import 'package:flutter/widgets.dart';
class RefreshFutureBuilder<T> extends StatefulWidget {
final Future<T> Function()? futureCreator;
final T? initialData;
final Widget Function(BuildContext context, Future Function() refresh, RefreshFutureBuilderSnapshot<T> snapshot) builder;
const RefreshFutureBuilder({ Key? key, this.futureCreator, this.initialData, required this.builder }) : super(key: key);
@override
_RefreshFutureBuilderState<T> createState() => _RefreshFutureBuilderState<T>();
}
class _RefreshFutureBuilderState<T> extends State<RefreshFutureBuilder<T>> {
late RefreshFutureBuilderSnapshot<T> snapshot;
Future<T> Function()? futureCreator;
bool _disposed = false;
@override
void initState() {
super.initState();
snapshot = widget.initialData != null ? RefreshFutureBuilderSnapshot.initial(widget.initialData!) : RefreshFutureBuilderSnapshot.nothing();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (futureCreator != widget.futureCreator) {
futureCreator = widget.futureCreator;
runFuture();
}
}
Future runFuture() async {
if (futureCreator == null) {
return;
}
// Set state to signify loading
setState(() {
switch (snapshot.state) {
case RefreshFutureBuilderState.none:
snapshot = RefreshFutureBuilderSnapshot.waiting();
break;
case RefreshFutureBuilderState.initial:
snapshot = RefreshFutureBuilderSnapshot.refresh(snapshot.data);
break;
case RefreshFutureBuilderState.waiting:
return;
case RefreshFutureBuilderState.error:
snapshot = RefreshFutureBuilderSnapshot.waiting();
break;
case RefreshFutureBuilderState.done:
snapshot = RefreshFutureBuilderSnapshot.refresh(snapshot.data);
break;
case RefreshFutureBuilderState.refreshing:
return;
case RefreshFutureBuilderState.refreshError:
snapshot = RefreshFutureBuilderSnapshot.refresh(null, snapshot.error, snapshot.stackTrace);
break;
default:
}
});
try {
final data = await futureCreator!();
if (_disposed) {
return;
}
setState(() {
snapshot = RefreshFutureBuilderSnapshot.withData(data);
});
}
catch (e, st) {
if (_disposed) {
return;
}
setState(() {
if (snapshot.state == RefreshFutureBuilderState.waiting) {
snapshot = RefreshFutureBuilderSnapshot.withError(e, st);
}
else {
snapshot = RefreshFutureBuilderSnapshot.refreshError(snapshot.data, e, st);
}
});
}
}
@override
void dispose() {
_disposed = true;
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.builder(
context,
runFuture,
snapshot,
);
}
}
class RefreshFutureBuilderSnapshot<T> {
final RefreshFutureBuilderState state;
final T? data;
final Object? error;
final StackTrace? stackTrace;
bool get hasData => data != null;
bool get hasError => error != null;
const RefreshFutureBuilderSnapshot._(this.state, this.data, this.error, this.stackTrace);
const RefreshFutureBuilderSnapshot.nothing() : state = RefreshFutureBuilderState.none, data = null, error = null, stackTrace = null;
const RefreshFutureBuilderSnapshot.initial(this.data) : state = RefreshFutureBuilderState.initial, error = null, stackTrace = null;
const RefreshFutureBuilderSnapshot.waiting() : state = RefreshFutureBuilderState.waiting, data = null, error = null, stackTrace = null;
const RefreshFutureBuilderSnapshot.withError(this.error, [this.stackTrace]) : state = RefreshFutureBuilderState.error, data = null;
const RefreshFutureBuilderSnapshot.withData(this.data) : state = RefreshFutureBuilderState.done, error = null, stackTrace = null;
const RefreshFutureBuilderSnapshot.refresh(this.data, [this.error, this.stackTrace]) : state = RefreshFutureBuilderState.refreshing;
const RefreshFutureBuilderSnapshot.refreshError(this.data, this.error, this.stackTrace) : state = RefreshFutureBuilderState.refreshError;
}
enum RefreshFutureBuilderState {
none,
initial,
waiting,
error,
done,
refreshing,
refreshError,
}