stabilized LoadableStateConsumer widget hierarchy to prevent scroll resets, added pull-to-refresh configuration, and disabled it in chat view
This commit is contained in:
@@ -21,6 +21,7 @@ class LoadableStateConsumer<
|
||||
final Widget Function(TState state, bool loading) child;
|
||||
final void Function(TState state)? onLoad;
|
||||
final bool wrapWithScrollView;
|
||||
final bool enablePullToRefresh;
|
||||
|
||||
/// Optional predicate for callers whose [TState] always contains a non-null
|
||||
/// envelope but where actual content (e.g. a nested response) is loaded
|
||||
@@ -33,6 +34,7 @@ class LoadableStateConsumer<
|
||||
required this.child,
|
||||
this.onLoad,
|
||||
this.wrapWithScrollView = false,
|
||||
this.enablePullToRefresh = true,
|
||||
this.isReady,
|
||||
super.key,
|
||||
});
|
||||
@@ -62,29 +64,34 @@ class LoadableStateConsumer<
|
||||
final showError = hasError && !hasContent;
|
||||
final showErrorBar = hasError && hasContent;
|
||||
|
||||
var childWidget = ConditionalWrapper(
|
||||
condition: loadableState.reFetch != null,
|
||||
wrapper: (child) => RefreshIndicator(
|
||||
onRefresh: () {
|
||||
if (loadableState.reFetch != null) loadableState.reFetch!();
|
||||
return Future.value();
|
||||
},
|
||||
child: ConditionalWrapper(
|
||||
// Keep the wrapper hierarchy stable across refresh cycles. The bloc clears
|
||||
// reFetch to null while a refetch is in flight and restores it on
|
||||
// completion; flipping the RefreshIndicator in and out on that signal
|
||||
// would change the widget tree under the ListView and reset its scroll
|
||||
// position every refresh.
|
||||
final content = SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: hasContent
|
||||
? child(typedData as TState, isLoading)
|
||||
: const SizedBox.shrink(),
|
||||
);
|
||||
final scrollable = ConditionalWrapper(
|
||||
condition: wrapWithScrollView,
|
||||
wrapper: (child) => SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: child,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: hasContent
|
||||
? child(typedData as TState, isLoading)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
child: content,
|
||||
);
|
||||
final childWidget = enablePullToRefresh
|
||||
? RefreshIndicator(
|
||||
onRefresh: () {
|
||||
loadableState.reFetch?.call();
|
||||
return Future.value();
|
||||
},
|
||||
child: scrollable,
|
||||
)
|
||||
: scrollable;
|
||||
|
||||
return BlocModule<LoadableStateBloc, LoadableStateState>(
|
||||
create: (context) => LoadableStateBloc(),
|
||||
|
||||
@@ -375,6 +375,7 @@ class _ChatViewState extends State<ChatView> with RouteAware {
|
||||
isReady: (state) =>
|
||||
state.chatResponse != null &&
|
||||
state.currentToken == widget.room.token,
|
||||
enablePullToRefresh: false,
|
||||
child: (state, _) {
|
||||
final items =
|
||||
_buildMessages(state.chatResponse!).reversed.toList();
|
||||
|
||||
Reference in New Issue
Block a user