You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将异步Dispatch方法作为RefreshIndicator的onRefresh回调

我来帮你把这个下拉刷新结合Redux和Firestore的流程理顺并补全细节,咱们一步步来:

1. 完善Redux Action与Reducer逻辑

首先把你写了一半的fetchPostsRequest Action补全,还要配套对应的Action类型和Reducer处理逻辑,这样才能正确更新状态:

补全Action实现

// 定义Action类型枚举
enum PostsActionType { loading, loaded, failed }

// 加载中Action
class PostsLoadingAction {
  final type = PostsActionType.loading;
}

// 加载成功Action
class PostsLoadedAction {
  final type = PostsActionType.loaded;
  final List<Post> posts;
  PostsLoadedAction({required this.posts});
}

// 加载失败Action
class PostsLoadFailedAction {
  final type = PostsActionType.failed;
  final String error;
  PostsLoadFailedAction({required this.error});
}

// 你的获取帖子Action
final fetchPostsRequest = () {
  return (Store<AppState> store) async {
    // 先发送加载中状态,让UI显示加载提示
    store.dispatch(PostsLoadingAction());
    
    try {
      final postCollection = Firestore.instance.collection("posts");
      final snapshot = await postCollection.getDocuments();
      // 把Firestore文档转成你的Post模型类(需要自己实现fromFirestore方法)
      final posts = snapshot.documents.map((doc) => Post.fromFirestore(doc)).toList();
      // 发送加载成功Action更新状态
      store.dispatch(PostsLoadedAction(posts: posts));
    } catch (error) {
      // 处理错误,发送失败Action
      store.dispatch(PostsLoadFailedAction(error: error.toString()));
    }
  };
};

对应的Reducer处理

// 假设你的AppState包含PostsState
class AppState {
  final PostsState postsState;
  AppState({required this.postsState});

  AppState copyWith({PostsState? postsState}) {
    return AppState(postsState: postsState ?? this.postsState);
  }
}

// 帖子状态类
class PostsState {
  final bool isLoading;
  final List<Post>? posts;
  final String? error;

  PostsState({
    required this.isLoading,
    this.posts,
    this.error,
  });

  PostsState copyWith({
    bool? isLoading,
    List<Post>? posts,
    String? error,
  }) {
    return PostsState(
      isLoading: isLoading ?? this.isLoading,
      posts: posts ?? this.posts,
      error: error ?? this.error,
    );
  }
}

// 根Reducer处理帖子相关Action
AppState appReducer(AppState state, dynamic action) {
  if (action is PostsLoadingAction) {
    return state.copyWith(
      postsState: state.postsState.copyWith(
        isLoading: true,
        error: null,
      ),
    );
  } else if (action is PostsLoadedAction) {
    return state.copyWith(
      postsState: state.postsState.copyWith(
        isLoading: false,
        posts: action.posts,
      ),
    );
  } else if (action is PostsLoadFailedAction) {
    return state.copyWith(
      postsState: state.postsState.copyWith(
        isLoading: false,
        error: action.error,
      ),
    );
  }
  return state;
}
2. 配置RefreshIndicator与Redux的联动

在你的列表组件中,通过StoreConnector连接Redux store,把状态和Action触发方法传递给UI,同时给RefreshIndicator的onRefresh绑定触发逻辑:

class PostListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, _ViewModel>(
      converter: (store) => _ViewModel.fromStore(store),
      builder: (context, viewModel) {
        return Scaffold(
          body: RefreshIndicator(
            // 下拉时触发获取数据的Action
            onRefresh: () => viewModel.fetchPosts(),
            child: _buildList(viewModel),
          ),
        );
      },
    );
  }

  Widget _buildList(_ViewModel viewModel) {
    // 根据状态渲染不同UI
    if (viewModel.isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (viewModel.error != null) {
      return Center(child: Text('加载失败:${viewModel.error}'));
    }

    if (viewModel.posts == null || viewModel.posts!.isEmpty) {
      return const Center(child: Text('暂无帖子'));
    }

    return ListView.builder(
      itemCount: viewModel.posts!.length,
      itemBuilder: (context, index) {
        final post = viewModel.posts![index];
        return ListTile(
          title: Text(post.title),
          subtitle: Text(post.content),
        );
      },
    );
  }
}

// ViewModel用于封装UI需要的状态和方法
class _ViewModel {
  final bool isLoading;
  final List<Post>? posts;
  final String? error;
  final Future<void> Function() fetchPosts;

  _ViewModel({
    required this.isLoading,
    required this.posts,
    required this.error,
    required this.fetchPosts,
  });

  static _ViewModel fromStore(Store<AppState> store) {
    return _ViewModel(
      isLoading: store.state.postsState.isLoading,
      posts: store.state.postsState.posts,
      error: store.state.postsState.error,
      // 绑定触发Action的方法,返回Future让RefreshIndicator知道何时结束刷新
      fetchPosts: () async {
        await store.dispatch(fetchPostsRequest());
      },
    );
  }
}
3. 关键细节提醒
  • 确保你的Post模型类有fromFirestore方法,能把Firestore的DocumentSnapshot正确转成实体对象
  • onRefresh必须返回一个Future,这样RefreshIndicator才能在异步操作完成后停止刷新动画,所以我们把Action改成了异步形式

内容的提问来源于stack exchange,提问作者haz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:32:37