从 RN 到 Flutter-部件

在 Flutter 中,你应该使用部件去描述针对于现有状态和配置的界面。

部件是由许多很小或者单一目的的部件组成。比如Container部件就包括几个相关布局、绘制、定位和定型组件,像是LimitedBoxConstrainedBoxAlignPaddingDecoratedBox以及Transform部件。除了使用Container来实现效果,你也可以用几个组件来实现它。

Center部件是另一个控制布局的例子。想要剧中部件,就用Center包住它。这些部件没有自己的展示,他们的目标就是控制内部部件的布局。想要理解部件是如何渲染,可以去查看他们的相邻部件。

了解更多信息,可以查看Flutter 技术概览

想要了解更多部件信息,可以查看Flutter 基础部件Flutter 部件目录 或者 Flutter 部件索引

界面

Flutter 中的 View 组件

在 React Native 中,View是支持Flexbox布局、样式处理、触摸处理和无障碍化的组件。

在 Flutter 中,你可以使用诸如ContainerColumn RowCenter的核心布局部件。更多信息可以参考布局部件目录。

Flutter 中的 FlatListSectionList 组件

一个List是垂直排布的可滚动列表。

在 React Native 中,FlatListSectionList可以用来渲染简单的或者复杂内容的列表。

<FlatList data={[]} renderItem={({ item }) => <Text>{item.key}</Text>} />

ListView是 Flutter 中最常被使用的滚动部件。默认的构造器接受一列准确定义的数据。ListView适合少量个数部件渲染。对于无限滚动,需要使用ListView.builder,它会按需渲染,并之渲染能展示出来的数据。

var data = [];
ListView.builder(
    itemCount: data.length,
    itemBuilder: (context, int index) {
        return Text(data[index]);
    },
);
Adroid ListVIewiOS ListView
Android ListViewiOS ListView

阅读你的第一个 Flutter 应用,第一部分更多了解如何实现无限滚动。

如何使用画布

在 React Native 中是没有相关组件的,需要引入类似于react-native-canvas的第三方组件。

const App = () => {
  return (
    <View>
      <Canvas
        ref={(canvas) => {
          const ctx = canvas.getContext("2d");
          ctx.fillStyle = "skyblue";
          ctx.beginPath();
          ctx.arc(75, 75, 50, 0, 2 * Math.PI);
          ctx.fillRect(150, 100, 300, 300);
          ctx.stroke();
        }}
      />
    </View>
  );
};

在 Flutter 中,你可以使用CustomPaintCustomPainter类去绘制画布。

下面的例子展示的是如何使用CustomPaint部件绘制。它实现了虚类 CustomPainter,并传递了 CustomPaint 的属性。CustomPaint 的子类必须实现 paint()shouldRepaint() 方法。

class MyCanvasPainter extends CustomPainter {
    paint(Canvas anvas, Size size) {
        {
            Paint paint = Paint();
            paint.color = Colors.amber;
            canvas.drawCircle(Offset(100.0, 200.0), 40.0, paint);
        }
        {
            Paint paint = Paint();
            paint.color = Colors.lightBlue;
            Rect rect = Rect.fromPoints(Offset(150.0, 300.0), Offset(300.0, 400.0));
            canvas.drawRect(rect, paintRect);
        }
    }
    shouldRepaint() => false;
}

Widget getCanvas() {
    return Scaffold(
        body: CustomPaint(
            paint: MyCanvasPainter(),
        ),
    );
}
Android CustomPaintiOS CustomPaint
Android CustomPaintiOS CustomPaint

布局

如何使用布局部件

在 React Native,许多布局可以通过 props 传入。比如,你可以使用Viewstyle属性去指定 flexbox 布局。想要让组件成列排列,可以指定样式为flexDirection: "column"

<View
    style={{
        flex: 1,
        flexDirection: "column",
        justifyContent: "space-between",
        alignItems: "center"
    }}
/>

在 Flutter 中,布局主要是由既定的布局部件和他们的参数实现。

举个例子,ColumnRow组件接受一个数组作为参数来以列或者以行排列。一个Container部件既能处理样式也能处理布局,一个Center部件可以将组件居中。

Center(
    child: Column(
        children: <Widget> [
            Container(
                color: Colors.red,
                width: 100.0,
                height: 100.0,
            ),
            Container(
                color: Colors.blue,
                width: 100.0,
                height: 100.0
            ),
            Container(
                color: Colors.green,
                width: 100.0,
                height: 100.0,
            ),
        ],
    ),
)

Flutter 提供一堆布局部件,比方说,PaddingAlignStack

了解全部的布局部件,可以查看布局部件

Adroid LayoutiOS Layout
Adroid Layout iOS Layout

如何堆叠部件

在 React Native,可以使用绝对定位absolute来堆叠组件。

Flutter 使用Stack部件让部件按照图层分布。部件会部分覆盖于下面的部件。

Stack(
    alignment: cont Alignment(0.6, 0.6),
    children: <Widget>[
        CircleAvatar(
            backgroundImage: NetworkImage(
                'https://avatars3.githubusercontent.com/u/14101776?v=4'
            )
        ),
        Container(
            decoration: BoxDecoration(
                color: Colors.black45,
            ),
            child: Text("Flutter"),
        ),
    ],
)

上面的例子使用Stack来布局一个Container并展示一个拥有黑色半透明背景的Text部件覆盖于CircleAvatar部件。这个部件通过对齐参数来规定文字位置。

Android StackiOS Stack
Android StackiOS Stack

更多信息可以查看Stack对象文档

样式

如何处理组件样式

在 React Native 中,内联样式和stylesheets.create是用来处理组件样式的。

const style = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

<View style={styles.container}>
  <Text style={{ fontSize: 32, color: "cyan", fontWeight: "600" }}>
    This is a simple text
  </Text>
</View>;

在 Flutter 中,Text部件可以使用TextStyle类,这个类的对象也可以给多个部件复用。

var textStyle = TextStyle(
    fontSize: 32.0,
    color: Colors.cyan,
    fontWeight: FontWeight.w600,
);

Center(
    child: Column(
        children: <Widget>[
            Text(
                'sample text',
                style: textStyle,
            ),
            Padding(
                padding: EdgeInsets.all(20.0),
                child: Icon(
                    Icons.lightbulb_outline,
                    size: 48.0,
                    color: Colors.redAccent,
                ),
            ),
        ],
    ),
)
Android StyleiOS Style
Android StyleiOS Style

如何使用图标和颜色

React Native 没有支持图标的库(这点我不是很同意)。

在 Flutter 中,引入 Material 库包含一堆Material 图标颜色

Icon(Icons.lightbulb_outline, color: COlors.redAccent)

使用Icons类时,记住要把uses-material-design: true设置在pubspec.yaml中。这保证MaterialIcons字体会被包括在应用中。

name: my_awesome_application
flutter:
  uses-material-design: true

Flutter 的Cupertino包,完全遵守 iOS 设计语言。要使用CupertinoIcons字体,在项目中增加cupertino_icons依赖。

name: my_awesome_application
dependencies:
  cupertino_icons: ^0.1.0

要完全的自定义组件的色彩和样式,使用ThemeData来定义主题。设置MaterialAppThemeData对象。Colors类提供遵守 Material 设计语言的调色盘。

Widget build() {
    return MaterialApp(
        title: "Sample App",
        theme: ThemeData(
            primarySwatch: Colors.blue,
            textSelectionColor: Colors.red,
        ),
        home: SampleAppPage()
    );
}

如何增加主题样式

在 Ract Native,主题是组件定义好的。

在 Flutter 中,使用ThemeData类为整个MaterialApp部件提供主题。

Widget build() {
    return MaterialApp(
        title: "Sample App",
        theme: ThemeData(
            primarySwatch: Colors.blue,
            textSelectionColor: Colors.red,
        ),
        home: SampleAppPage()
    );
}

一个Theme甚至可以不依赖于MaterialApp部件。Theme部件需要一个ThemeData传入它的data参数中,以适配它的所有子部件。

class SampleTheme extends StatelessWidget {
    Widget build(BuildContext context) {
      return Theme(
           data: ThemeData(
               primaryColor: Colors.cyan,
               brightness: brightness,
           ),
           child: Scaffold(
               backgroundColor: Theme.of(context).primaryColor,
           )
       )
    }
}