从Redux将项目动态添加到ListView

时间:2020-03-04 15:06:22

标签: flutter flutter-listview flutter-redux flutter-state

构建一个社交网络应用程序,我遇到以下问题:

其中一个屏幕具有 Feed 。有些帖子是从API中获取的,但可用于分页。第一个请求获得前10个帖子。一旦到达ListView的底部,它将获取接下来的10条帖子,依此类推。

问题是,如何在不重建页面的情况下将新获取的帖子追加到ListView中。由于数据来自Redux状态。现在可以使用,但是当从API获取新帖子时,滚动位置将返回顶部,因为整个列表都已重新构建。这是代码:

app_state.dart

@immutable
class AppState {
  final FeedState feedState;

  AppState({
    @required this.feedState,
  });

  factory AppState.initial() {
    return AppState(
      feedState: FeedState.initial(),
    );
  }

  AppState copyWith({
    FeedState feedState,
  }) {
    return AppState(
      feedState: feedState ?? this.feedState,
    );
  }
}

@immutable
class FeedState {
  List<Post> feed;
  bool error;
  bool loading;

  FeedState({
    this.feed,
    this.error,
    this.loading,
  });

  factory FeedState.initial() {
    return FeedState(
      feed: null,
      error: false,
      loading: false,
    );
  }

  FeedState copyWith({
    List<Post> feed,
    bool error,
    bool loading,
  }) {
    return FeedState(
      feed: feed ?? this.feed,
      error: error ?? this.error,
      loading: loading ?? this.loading,
    );
  }
}

feed_reducer.dart

final feedReducer = combineReducers<FeedState>([
  TypedReducer<FeedState, FeedSuccessAction>(_feedSuccess),
  TypedReducer<FeedState, FeedAppendSuccessAction>(_feedAppendSuccess),
  TypedReducer<FeedState, FeedFailedAction>(_feedFailed),
  TypedReducer<FeedState, StartLoadingAction>(_startLoading),
]);

FeedState _feedSuccess(FeedState state, FeedSuccessAction action) {
  return state.copyWith(feed: action.feed, loading: false, error: false);
}

FeedState _feedAppendSuccess(FeedState state, FeedAppendSuccessAction action) {
  return state.copyWith(
      feed: state.feed + action.feed, loading: false, error: false);
}

FeedState _feedFailed(FeedState state, FeedFailedAction action) {
  return state.copyWith(feed: null, loading: false, error: true);
}

FeedState _startLoading(FeedState state, StartLoadingAction action) {
  return state.copyWith(loading: true, error: false);
}

feed_actions.dart

class StartLoadingAction {
  StartLoadingAction();
}

class FeedSuccessAction {
  final List<Post> feed;

  FeedSuccessAction(this.feed);
}

class FeedAppendSuccessAction {
  final List<Post> feed;

  FeedAppendSuccessAction(this.feed);
}

class FeedFailedAction {
  FeedFailedAction();
}

ThunkAction fetchFeed(int page) {
  return (Store store) async {
    new Future(() async {
      store.dispatch(new StartLoadingAction());

      try {
        List<Post> feed = await Api().fetchFeed(page);
        store.dispatch(new FeedSuccessAction(feed));
      } catch (error) {
        store.dispatch(new FeedFailedAction());
      }
    });
  };
}

ThunkAction fetchAppendFeed(int page) {
  return (Store store) async {
    new Future(() async {
      store.dispatch(new StartLoadingAction());

      try {
        List<Post> feed = await Api().fetchFeed(page);
        store.dispatch(new FeedAppendSuccessAction(feed));
      } catch (error) {
        store.dispatch(new FeedFailedAction());
      }
    });
  };
}

feed_screen.dart

class FeedScreen extends StatelessWidget {
final ScrollController feedController = ScrollController();
int page = 0;

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Feed"),
      ),
      body: StoreConnector<AppState, FeedViewModel>(
        converter: (Store<AppState> store) => FeedViewModel.fromStore(store),
        builder: (BuildContext context, FeedViewModel vm) {
          if (!vm.loading && !vm.error && vm.feed == null) {
            vm.getFeed(0);
            feedController.addListener(() => listener(vm.appendFeed));
          }

          if (vm.loading) return buildLoading();
          if (vm.error) return buildError(vm);
          if (vm.feed != null) {
            return RefreshIndicator(
              child: buildFeed(vm),
              onRefresh: () {
                page = 0;
                vm.getFeed(page);
              },
            );
          }
          if (vm.feed != null && vm.feed.length <= 0) return buildInitial(vm);
          return Container();
        },
      ),
    );
  }

   Widget buildFeed(FeedViewModel model) {
    List<Feed> feed = model.feed;

    return ListView.builder(
      controller: feedController,
      itemCount: feed.length,
      itemBuilder: (context, index) {
          ... // code
      },
    );
}

class FeedViewModel {
  final bool loading;
  final bool error;
  final List<Post> feed;

  final Function(int) getFeed;
  final Function(int) appendFeed;

  FeedViewModel({
    this.loading,
    this.error,
    this.feed,
    this.getFeed,
    this.appendFeed,
  });

  static FeedViewModel fromStore(Store<AppState> store) {
    return FeedViewModel(
      loading: store.state.feedState.loading,
      error: store.state.feedState.error,
      feed: store.state.feedState.feed,
      getFeed: (int page) {
        store.dispatch(fetchFeed(page));
      },
      appendFeed: (int page) {
        store.dispatch(fetchAppendFeed(page));
      },
    );
  }
}

0 个答案:

没有答案