Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter: 使用 Redux 实现无限 ListView #6388

Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 32 additions & 30 deletions TODO1/flutter-infinite-listview-with-redux.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,36 @@
> * 原文作者:[Pavel Sulimau](https://medium.com/@pavel.sulimau)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/flutter-infinite-listview-with-redux.md](https://github.com/xitu/gold-miner/blob/master/TODO1/flutter-infinite-listview-with-redux.md)
> * 译者:
> * 校对者:
> * 译者:[Xat_MassacrE](https://github.com/XatMassacrE)
> * 校对者:[TsichiChang](https://github.com/TsichiChang)

# Flutter: Infinite ListView with Redux
# Flutter: 使用 Redux 实现无限滚动的 ListView

![](https://cdn-images-1.medium.com/max/3840/1*spVWmt32pcQItXguvspQqw.jpeg)

## Motivation
## 动机

If you need to implement an app with more than one screen the odds are that one of the screens will represent its data in some form of a list. I’d like to show you how you can develop an “infinite” list of items with “pull-to-refresh” and “error-handling” on top of **Flutter** + **Redux**.
如果你需要实现一个具有多页面的应用程序,而且其中的一个页面需要用列表的形式来展示数据。那么我将会告诉你如何基于 **Flutter** + **Redux** 来开发出一个具有 『下拉刷新』和『错误处理』功能的『无限』列表应用。

## Prerequisites
## 预备知识

Make sure you feel comfortable with the terms described in the docs in [redux.dart](https://github.com/johnpryan/redux.dart) and [flutter_redux](https://github.com/brianegan/flutter_redux) repositories. I would also suggest that you take a look at [my previous article](https://medium.com/flutter-community/flutter-redux-toast-notification-fcd0971eaf0f).
首先要确保你对 [redux.dart](https://github.com/johnpryan/redux.dart) [flutter_redux](https://github.com/brianegan/flutter_redux) 这两个文档中的术语有足够的了解。其次建议你阅读一下[我之前的文章](https://medium.com/flutter-community/flutter-redux-toast-notification-fcd0971eaf0f)

## Goal
## 目标

An app that displays [issues from the flutter GitHub repository](https://github.com/flutter/flutter/issues) sounds reasonable to me for the sake of this demo.
实现一个展示 [Flutter issues 列表](https://github.com/flutter/flutter/issues) demo

Here is the result that will be achieved in the end:
下图就是我们实现之后的样子。

![](https://cdn-images-1.medium.com/max/2000/1*EdwqcExhCgZYHytAU-sUxA.gif)

## Development
## 开发

We’ll need [flutter_redux](https://pub.dev/packages/flutter_redux) and [http](https://pub.dev/packages/http) packages, so get them added to your **pubspec.yaml** file and installed. Also, [intl](https://pub.dev/packages/intl) package and [redux_logging](https://pub.dev/packages/redux_logging) packages will be useful for date formatting and debugging purposes respectively.
首先我们需要 [flutter_redux](https://pub.dev/packages/flutter_redux) [http](https://pub.dev/packages/http) 包,将这两个包添加到 **pubspec.yaml** 文件中并安装它们。同时,[intl](https://pub.dev/packages/intl) [redux_logging](https://pub.dev/packages/redux_logging) 这两个包对于日期格式化以及调试也是非常有用的。

#### Model

It’s just a simple class for holding some info about a Github issue. It also can initialize itself from a piece of JSON.
Model 是一个包含 Github issue 属性的简单类,同时也可以通过 JSON 来实例化。

```Dart
import 'package:intl/intl.dart';
Expand All @@ -53,7 +53,7 @@ class GithubIssue {

#### State

There is not so much data that we’ll need to keep inside the state: the list of items, the flags indicating whether the data is being loaded and whether there is more data available, and the error.
我们需要在 state 中记录的数据并不多:一个包含多个 issue 的列表,一个数据是否被载入的标志位,一个是否还有更多数据的标志位以及错误信息。

```Dart
import 'package:flutter_redux_infinite_list/models/github_issue.dart';
Expand Down Expand Up @@ -105,10 +105,11 @@ class AppState {
```

You may have noticed the `toString` method. It can be used for debugging purposes and comes in handy if you decide to use `LoggingMiddleware`.
你们会注意到这里有一个 `toString` 方法。它的主要作用是调试和未来更方便的使用 `LoggingMiddleware`。

#### Actions

There are two actions for dealing with actual data and two actions for dealing with a possible error.
这里有两个 actions 用来处理真实数据,还有两个 actions 用来处理可能的错误。

```Dart
import 'package:flutter/cupertino.dart';
Expand Down Expand Up @@ -142,7 +143,7 @@ class ErrorHandledAction {}

#### Reducers

The reducers that create a new state based on a received action are a bit more complicated than actions, but only a bit. They are just simple pure functions that are combined by the `combineReducers` function the library gives us.
Reducer 会根据接收到的 action 创建新的 state,它会比 action 复杂一点,但是也并没有复杂很多。它们其实就是一些由 Redux 库提供的 `combineReducers` 函数结合起来的纯函数而已。

```Dart
import 'actions.dart';
Expand Down Expand Up @@ -207,7 +208,7 @@ Exception _errorHandledReducer(Exception _, ErrorHandledAction action) {

#### Middleware

The implemented middleware basically consists of the function that tries to load data from the API and posts either the successful action or the action indicating a failure.
这里使用的 middleware 基本上是由加载数据的 API 函数和发送成功或失败的 action 构成的。

```Dart
import 'dart:convert';
Expand Down Expand Up @@ -256,7 +257,7 @@ Future<List<GithubIssue>> _loadFlutterGithubIssues(

#### Container

Now we are getting closer to the presentation layer. Here is the container widget that is responsible for converting the latest **App State** to a `_ViewModel` and connecting the `_ViewModel` to the presentation widget.
现在,我们离视图层又近了一步。这里的 container 组件可以将最新的 **App State** 转换成一个 `_ViewModel` 并将 `_ViewModel` 与视图组件连接起来。

```Dart
import 'package:flutter_redux_infinite_list/models/github_issue.dart';
Expand Down Expand Up @@ -336,7 +337,7 @@ class _ViewModel {

#### Presentation

This is the part where things become more interesting. Let’s start with two tiny presentation components that will be utilized in the `HomeScreen`. They are `CustomProgressIndicator` and `GithubIssueListItem`.
这个部分将会更加有趣。让我们先从 `HomeScreen` 中用到的两个展示组件开始:`CustomProgressIndicator` `GithubIssueListItem`

```Dart
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -402,14 +403,14 @@ class GithubIssueListItem extends StatelessWidget {

```

**Here goes the core presentation logic.**
**下面是主要的展示逻辑**

**Pay more attention to the `HomeScreen`.** There are a few things worth noticing:
**注意一下 `HomeScreen`。**这里有好几个值得关注的点:

1. The screen involves the `ScrollController` to determine when we need to invoke the `loadNextPage` function.
2. Something called `Debouncer` is used (the implementation you’ll see below). It’s just a tiny class with a timer inside it that ensures that consecutive events from the `ScrollController` won’t create tons of requests for a next page, but rather the only request will be made in the specified period of time.
3. `RefreshIndicator` that helps us significantly to get the so-called “pull-to-refresh” feature.
4. `ErrorNotifier` that is responsible for showing a toast notification in case an error occurs. If you need more details on this one, take a look [at my previous article](https://medium.com/flutter-community/flutter-redux-toast-notification-fcd0971eaf0f).
1. 页面包含的 `ScrollController` 决定了是否需要调用 `loadNextPage` 函数。
2. 在这里使用了 `Debouncer`(具体实现见下文)。它是一个含有定时器功能的简单类,能够确保来自 `ScrollController` 的连续事件不会触发大量的下一页请求,而是在一个特定时间段之内只发送一次请求。
3. `RefreshIndicator` 可以在我们使用『下拉刷新』功能时给予提示。
4. 当发生错误的时候 `ErrorNotifier` 将会显示 toast 通知。如果你需要在该通知中更多的显示详细信息,可以看看[我之前的文章](https://medium.com/flutter-community/flutter-redux-toast-notification-fcd0971eaf0f)

```Dart
import 'package:flutter_redux_infinite_list/common/debouncer.dart';
Expand Down Expand Up @@ -506,7 +507,7 @@ class _HomeScreenState extends State<HomeScreen> {

```

This is the code for the `ErrorNotifer`.
下面是 `ErrorNotifer` 的代码。

```Dart
import 'package:flutter_redux_infinite_list/redux/actions.dart';
Expand Down Expand Up @@ -569,11 +570,11 @@ class _ViewModel {

```

And here is how the `ErrorNotifier` looks like in action.
下图就是在 action 中 `ErrorNotifier` 呈现的样子。

![](https://cdn-images-1.medium.com/max/2000/1*6cesoZFB8Hj9UaLQgffcKA.gif)

This is the `Debouncer` that was mentioned above.
接下来就是上文提到的 `Debouncer`

```Dart
import 'dart:async';
Expand All @@ -596,7 +597,7 @@ class Debouncer {
}
```

Finally, here is the `main.dart` file that combines all the described components.
最后就是把所有声明的组件都结合起来的 `main.dart` 文件。

```Dart
import 'package:flutter_redux_infinite_list/redux/containers/home_container.dart';
Expand Down Expand Up @@ -633,8 +634,9 @@ class App extends StatelessWidget {
```

Don’t hesitate to give this sample a go yourself, grab the sources from the [Github repo](https://github.com/Pavel-Sulimau/flutter_redux_infinite_list).
不要犹豫,自己一定要去试一试,你可以从 [Github repo](https://github.com/Pavel-Sulimau/flutter_redux_infinite_list) 获取本文源码。

## Sources:
## 参考资源:

* [https://medium.com/filledstacks/flutter-redux-quick-start-3f549f5b05c5](https://medium.com/flutter-community/flutter-redux-toast-notification-fcd0971eaf0f)
* [https://github.com/johnpryan/redux.dart](https://github.com/johnpryan/redux.dart)
Expand Down