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.
136 lines
4.4 KiB
136 lines
4.4 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, Future Function(Future<T> Function()) replaceFuture, 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); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
Future replaceFutureCreator(Future<T> Function() newFutureCreator) { |
|
futureCreator = newFutureCreator; |
|
return runFuture(); |
|
} |
|
|
|
@override |
|
void dispose() { |
|
_disposed = true; |
|
super.dispose(); |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return widget.builder( |
|
context, |
|
runFuture, |
|
replaceFutureCreator, |
|
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, |
|
}
|
|
|