列表视图和适配器

列表视图和适配器

在 Flutter 中,与列表视图等价的是什么?

在Flutter中与 ListView 等价的是……一个 ListView!

在一个 Xamarin.Forms 的 ListView 中,你可以创建一个 ViewCell 可能还有一个 DataTemplateSelector 并将其传递到 ListView 中,该视图将用您的DataTemplateSelector 或者 ViewCell 的返回数据渲染每一行。但是,您通常必须确保打开单元格回收,否则会遇到内存问题和会使滚动速度变慢。

由于 Flutter 的不可变的 widget 模式,您将一个 widget 列表传递给您的 ListView,Flutter 会负责确保滚动速度快且平稳。

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row $i")));
    }
    return widgets;
  }
}

如何知道哪个列表项被点击了?

在 Xamarin.Forms 中,ListView 拥有一个ItemTapped 方法能找出哪个列表项被单击了。您可能还使用了许多其他技术,比如检查 SelectedItem 或 EventToCommand 的行为何时会发生更改。

在 Flutter 中,使用传入 widget 提供的触摸处理。

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new GestureDetector(
        child: new Padding(
            padding: new EdgeInsets.all(10.0),
            child: new Text("Row $i")),
        onTap: () {
          print('row tapped');
        },
      ));
    }
    return widgets;
  }
}

如何动态更新 ListView ?

在 Xamarin.Forms 中,如果将 ItemsSource 属性绑定到一个 ObservableCollection,就只需要更新视图模型中的列表。另一种方法是,你可以给属性 ItemsSource 分配一个新的 列表 。

在 Flutter 中,情况略有不同。如果您要在 setState() 内更新 widget 列表,您将很快看到您的数据在视觉上没有发生变化。这是因为当 setState() 被调用时,Flutter 的渲染引擎会检查 widget 树是否发生了更改。当它到达您的 ListView 时,会执行 == 检查,并确定这两个 ListView 是相同的。没有任何更改,就不需要更新。

要更新 ListView 的有一个简单方法,请在 setState() 中创建一个新 列表 ,并将数据从旧列表复制到新列表。虽然这种方法很简单,但不推荐用于大型数据集,如下例所示。

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: widgets),
    );
  }

  Widget getRow(int i) {
    return new GestureDetector(
      child: new Padding(
          padding: new EdgeInsets.all(10.0),
          child: new Text("Row $i")),
      onTap: () {
        setState(() {
          widgets = new List.from(widgets);
          widgets.add(getRow(widgets.length + 1));
          print('row $i');
        });
      },
    );
  }
}

推荐的、高效的、有效的列表构建方法是使用 ListView.Builder。在您有一个动态列表或一个包含大量数据的列表时,这种方法非常棒。这基本上相当于 Android 上的 RecyclerView,它会自动回收列表元素:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: new ListView.builder(
            itemCount: widgets.length,
            itemBuilder: (BuildContext context, int position) {
              return getRow(position);
            }));
  }

  Widget getRow(int i) {
    return new GestureDetector(
      child: new Padding(
          padding: new EdgeInsets.all(10.0),
          child: new Text("Row $i")),
      onTap: () {
        setState(() {
          widgets.add(getRow(widgets.length + 1));
          print('row $i');
        });
      },
    );
  }
}

与创建一个“列表视图” 相比,创建一个 ListView.builder需要接受两个关键参数:列表的初始长度和 ItemBuilder 函数。

ItemBuilder 函数类似于 Android 适配器中的 getView 函数;它接受一个位置,并返回您希望的在该位置呈现的行。

最后,但也是最重要的,要注意 onTap() 函数不再重新创建列表,而是用 .add 添加给它的。

更多信息,请访问编写你的第一个 Flutter 应用程序,第1部分和 编写你的第一个 Flutter 应用程序,第2部分