美文网首页
Android 开发者的 Flutter 指南 — View

Android 开发者的 Flutter 指南 — View

作者: 敲代码的本愿 | 来源:发表于2019-02-25 15:05 被阅读100次

    View 在 Flutter 中对应什么?

    对应的是 Widget,一种声明和构建 UI 的方式。

    特点

    • Widget 仅存在于每一帧之间。当 Widget 或其状态需要改变刷新时,都会重新创建实例以生成新的 Widget 实例树;
    • 不可变,轻量级。不可变是轻量级的部分原因,Widget 不是视图,也不绘制,仅对实际视图中的对象进行描述;
    • 灵活且富有表现力的设计语言。 可以实现任何设计语言,如遵循 Material Design 设计标准且支持所有平台的 Material 组件库;类似 iOS 设计风格的 Cupertino widgets 等。

    如何更新 Widget ?

    Widget 本身不可变且不能直接更新,只能通过修改 Widget 的状态来达到效果(间接更新:重新构建需要改变的 Widget 以替代原来的)。

    由此引申出:
    StatelessWidget:无状态,如用于显示只加载一次的 Logo。
    StatefulWidget:有状态,如时常请求网络数据更新页面。

    不同点:
    StatefulWidget 含有一个可跨帧存取数据的 State 对象。

    Widget 可变化,则是有状态的。但子 Widget 是有状态的,其父 Widget 仍可以是 StatelessWidget 。

    如何布局 Widget ?

    通过 Widget 树实现。

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text("Sample App"),
        ),
        body: Center(
          child: MaterialButton(
            onPressed: () {},
            child: Text('Hello'),
            padding: EdgeInsets.only(left: 10.0, right: 10.0),
          ),
        ),
      );
    }
    

    可在 Flutter 提供的 Widget 目录 查看相关布局。

    如何从布局中移除或添加组件 ?

    Flutter 没有类似 addChild() 这种方法,但可通过控制 boolean 类型的标志,来创建显示不同 Widget。

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(SampleApp());
    }
    
    class SampleApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Sample App',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: SampleAppPage(),
        );
      }
    }
    
    class SampleAppPage extends StatefulWidget {
      SampleAppPage({Key key}) : super(key: key);
    
      @override
      _SampleAppPageState createState() => _SampleAppPageState();
    }
    
    class _SampleAppPageState extends State<SampleAppPage> {
      // Default value for toggle
      bool toggle = true;
      void _toggle() {
        setState(() {
          toggle = !toggle;
        });
      }
    
      _getToggleChild() {
        if (toggle) {
          return Text('Toggle One');
        } else {
          return MaterialButton(onPressed: () {}, child: Text('Toggle Two'));
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Sample App"),
          ),
          body: Center(
            child: _getToggleChild(),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _toggle,
            tooltip: 'Update Text',
            child: Icon(Icons.update),
          ),
        );
      }
    }
    

    如何让 Widget 产生动画 ?

    通过 animation 库中的动画 Widget 包裹 Widget 。
    使用 Animation<double> 子类 AnimationController 控制动画 pause、seek、stop 、reverse 。

    淡入淡出动画举例:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(FadeAppTest());
    }
    
    class FadeAppTest extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Fade Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyFadeTest(title: 'Fade Demo'),
        );
      }
    }
    
    class MyFadeTest extends StatefulWidget {
      MyFadeTest({Key key, this.title}) : super(key: key);
      final String title;
      @override
      _MyFadeTest createState() => _MyFadeTest();
    }
    
    class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
      AnimationController controller;
      CurvedAnimation curve;
    
      @override
      void initState() {
        super.initState();
        controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
        curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
              child: Container(
                  child: FadeTransition(
                      opacity: curve,
                      child: FlutterLogo(
                        size: 100.0,
                      )))),
          floatingActionButton: FloatingActionButton(
            tooltip: 'Fade',
            child: Icon(Icons.brush),
            onPressed: () {
              controller.forward();
            },
          ),
        );
      }
    }
    

    更多参阅 Animation & Motion widgetsAnimations tutorialAnimations overview

    如何使用 Canvas 绘制 ?

    与 Android 拥有类似的 Canvas API,因为都是基于低级渲染引擎 Skia 。

    两个相关类:

    • CustomPaint:
    • CustomPainter:通过实现自定义的算法来绘制(implements your algorithm to draw to the canvas)。

    可到 StackOverflow 查看 Collin 关于签名绘制的回答。

    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(home: DemoApp()));
    
    class DemoApp extends StatelessWidget {
      Widget build(BuildContext context) => Scaffold(body: Signature());
    }
    
    class Signature extends StatefulWidget {
      SignatureState createState() => SignatureState();
    }
    
    class SignatureState extends State<Signature> {
      List<Offset> _points = <Offset>[];
      Widget build(BuildContext context) {
        return GestureDetector(
          onPanUpdate: (DragUpdateDetails details) {
            setState(() {
              RenderBox referenceBox = context.findRenderObject();
              Offset localPosition =
              referenceBox.globalToLocal(details.globalPosition);
              _points = List.from(_points)..add(localPosition);
            });
          },
          onPanEnd: (DragEndDetails details) => _points.add(null),
          child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite),
        );
      }
    }
    
    class SignaturePainter extends CustomPainter {
      SignaturePainter(this.points);
      final List<Offset> points;
      void paint(Canvas canvas, Size size) {
        var paint = Paint()
          ..color = Colors.black
          ..strokeCap = StrokeCap.round
          ..strokeWidth = 5.0;
        for (int i = 0; i < points.length - 1; i++) {
          if (points[i] != null && points[i + 1] != null)
            canvas.drawLine(points[i], points[i + 1], paint);
        }
      }
      bool shouldRepaint(SignaturePainter other) => other.points != points;
    }
    

    如何自定义 Widget ?

    通过组合粒度更小的 Widget ,而非继承。

    class CustomButton extends StatelessWidget {
      final String label;
    
      CustomButton(this.label);
    
      @override
      Widget build(BuildContext context) {
        return RaisedButton(onPressed: () {}, child: Text(label));
      }
    }
    
    @override
    Widget build(BuildContext context) {
      return Center(
        child: CustomButton("Hello"),
      );
    }
    

    原文给 Android 开发者的 Flutter 指南,参考 Android 开发者参考
    此文目的:仅与 Android 对比,尽可能言简意骇的翻译,且不遗漏、不曲解词义。

    相关文章

      网友评论

          本文标题:Android 开发者的 Flutter 指南 — View

          本文链接:https://www.haomeiwen.com/subject/uyhwyqtx.html