美文网首页
Flutter学习(五)开发初体验

Flutter学习(五)开发初体验

作者: yanhooIT | 来源:发表于2020-04-01 22:11 被阅读0次

内部团队学习,以简单直接为主

创建Flutter项目

  • Flutter的项目名称不可以使用驼峰命名,可以使用下划线区分
  • 通过终端命令
cd 指定目录
// 创建Flutter项目
flutter create hello_flutter
  • 通过Android Studio创建 通过Android Studio创建

项目文件介绍

工程目录
  • .dart_tool:记录依赖库的信息
  • .idea:Android Studio是基于IDEA开发的
  • android:安卓工程
  • ios:iOS工程
  • lib:放我们编写的Flutter代码,里面默认有一个main.dart,它是我们Flutter启动的入口文件,里面有main函数
  • test:UnitTest
  • .gitignore:git忽略文件管理
  • .metadata:对Flutter版本做了记录,不要改动
  • .packages:依赖库生成文件
  • hello_flutter.iml:记录配置信息
  • pubspec.lock:依赖库生成文件
  • pubspec.yaml:设置flutter工程的依赖库
  • README.md:项目描述文件

runApp和Widget

// 启动App
void main() => runApp(MyApp());

/// 是Flutter内部提供的一个函数
/// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
/// 入参是一个Widget
void runApp(Widget app) {
  // 省略代码...
}
  • runApp是Flutter内部提供的一个函数,启动App的入口,这个函数的入参是一个Widget
  • Widget可以理解为小部件,Flutter中万物皆Widget
    • 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等,在Flutter里这些东西都是不同的Widget而已
    • 整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget来做
  • 导入Flutter默认已经给我们提供的material库,来使用其中的很多内置Widget
import 'package:flutter/material.dart';

material设计风格

  • material是Google公司推行的一套设计风格,或者叫设计语言设计规范等;
  • 里面有非常多的设计规范,eg:颜色、文字的排版、响应动画与过度、填充等等,在Flutter中高度集成了material风格的Widget
  • 在应用中,可以直接使用这些内置Widget来创建应用

自定义Widget来解决Flutter嵌套过多的问题

  • 在Flutter开发中,我们可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
    • StatelessWidget:没有数据改变的Widget,适用于做一些纯展示工作;
    • StatefulWidget:有数据改变的Widget,适用于有交互的复杂场景;
  • 这里我们先使用StatelessWidget来自定义Widget
    • 让自己创建的Widget继承自StatelessWidget;
    • StatelessWidget包含一个必须重写的方法:build方法;
class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
  }
}
  • build方法什么情况下被执行呢?
    • 当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时)
    • 当我们的父Widget(parent widget)发生改变时,子Widget也会被重新构建
    • 如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时

为什么定义到Widget中的数据一定是不可变的,需要使用final来修饰

  • Flutter就是这么设计的:一旦Widget中展示的数据发生变化,就重新构建整个Widget
  • Flutter中通过@immutable关键字来给类添加注解来限定定义到Widget中的数据一定是不可变的,所以成员变量需要使用final来修饰(这里涉及到Dart的元编程,后面再说),如下所示:
@immutable
abstract class Widget extends DiagnosticableTree {
    // 省略代码...
}

StatelessWidget

  • Android Studio的智能提示是区分大小写的,如:MaterialApp,如果打materialApp是不能正确进行智能提示的
  • StatelessWidget综合示例,注意看代码注释
/// 导入Flutter提供的Material库,可以使用其中很多内置Widget
import 'package:flutter/material.dart';

/// runApp是Flutter内部提供的一个函数
/// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
/// 入参是一个Widget
void main() => runApp(MyApp());

/// 自定义Widget来减少嵌套层级
class MyApp extends StatelessWidget {
  /// 继承自StatelessWidget必须实现此方法
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      /// 去除debug标记
      debugShowCheckedModeBanner: false,
      /// 设置主题
      theme: ThemeData(primaryColor: Colors.yellowAccent),
      /// Scaffold是脚手架
      home: Scaffold(
        /// 导航栏
        appBar: AppBar(
          title: Text("火信"),
        ),
        /// body是页面的内容部分
        body: HomeView(),
      ),
    );
  }
}

/// 自定义Widget - 首页Widget
class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// 界面整体边距
    return Padding(
      /// padding属性用于设置边距大小
      padding: const EdgeInsets.all(8.0),
      /// 如果这里使用Column会出错
      /// 因为在Flutter的布局中,内容是不能超出屏幕范围的,当超出时不会自动变成滚动效果,而是会报下面的错误
      ///
      /// 解决方案:Column换成ListView
      /// ListView可以让自己的子Widget变成滚动的效果
      child: ListView(
        children: <Widget>[
          ProductCell("火信1.0", "来火信,聊懂区块链",
              "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
          ProductCell("火信2.0", "来火信,聊懂区块链",
              "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
          ProductCell("火信3.0", "来火信,聊懂区块链",
              "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
        ],
      ),
    );
  }
}

/// 自定义Widget - 单个Cell Widget
class ProductCell extends StatelessWidget {
  final String title;
  final String desc;
  final String imageUrl;
  
  /// 构造函数
  ProductCell(this.title, this.desc, this.imageUrl);

  @override
  Widget build(BuildContext context) {
    /// Container用于设置内边距和边框
    return Container(
      padding: EdgeInsets.all(20),
      /// 通过decoration来设置边框
      decoration: BoxDecoration(border: Border.all()),
      child: Column(
        children: <Widget>[
          /// Text文本Widget
          Text(title, style: TextStyle(fontSize: 24)),
          Text(desc, style: TextStyle(fontSize: 18)),
          /// 文字图片的间距
          SizedBox(
            /// 设置一个height属性,可以增加一些距离
            height: 10,
          ),
          /// Image是图片Widget
          Image.network(imageUrl)
        ],
      ),
    );
  }
}
  • 运行效果 火信

StatefulWidget

  • Flutter将StatefulWidget设计成了两个类StatefulWidgetState
  • 一个类继承自StatefulWidget,作为Widget树的一部分
  • 一个类继承自State,用于记录变化的状态,并且根据状态的变化,构建出新的Widget
  • 之所以这样设计,是因为在Flutter中,只要数据改变了就需要rebuild Widget
  • StatefulWidget的综合示例,注意看代码注释
/// 导入Flutter提供的Material库,可以使用其中很多内置Widget
import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// MaterialApp提示区分大小写
    return MaterialApp(
      /// 去除debug标记
//      debugShowCheckedModeBanner: false,
      /// 设置主题
      theme: ThemeData(primaryColor: Colors.yellowAccent),
      home: HomeWidget(),
    );
  }
}

class HomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// Scaffold是脚手架,区分大小写
    return Scaffold(
      /// Scaffold提示区分大小写
      appBar: AppBar(
        title: Text("HuobiChat"),
      ),
      body: HomeView("你好,火信!"),
    );
  }
}

/// Widget是不加_: 暴露给外部使用
class HomeView extends StatefulWidget {
  final String message;

  HomeView(this.message);

  @override
  State<StatefulWidget> createState() {
    return _HomeViewState();
  }
}

/// 为什么Flutter在设计的时候StatefulWidget的build方法放在State中
///   1.build出来的Widget是需要依赖State中的变量(状态/数据)
///   2.在Flutter的运行过程中:
///     Widget是不断的销毁和创建的
///     当我们自己的状态发生改变时, 并不希望重新状态一个新的State

/// State是加_: 这样状态这个类只是给StatefulWidget内部使用
class _HomeViewState extends State<HomeView> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    /// 居中Widget
    return Center(
      /// Column小部件:当有垂直方向布局时,就使用它;
      child: Column(
        /// 居中
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          _getButtons(),
          Text(
            "当前计数:$_counter",
            style: TextStyle(fontSize: 25),
          ),
          Text("传递的信息:${widget.message}")
        ],
      ),
    );
  }

  Widget _getButtons() {
    /// Row小部件:当时水平方向布局时,就使用它
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        /// RaiseButton小部件:可以创建一个按钮,并且其中有一个onPressed属性是传入一个回调函数,当点击按钮时回调
        RaisedButton(
          child: Text(
            "+",
            style: TextStyle(fontSize: 20, color: Colors.white),
          ),
          color: Colors.pink,
          onPressed: () {
            /// 手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets
            setState(() {
              _counter++;
            });
          },
        ),
        RaisedButton(
            child: Text(
              "-",
              style: TextStyle(fontSize: 20, color: Colors.white),
            ),
            color: Colors.purple,
            onPressed: () {
              setState(() {
                _counter--;
              });
            }),
      ],
    );
  }
}
  • 运行效果


    HuobiChat

生命周期

StatelessWidget生命周期
  • 代码演示
import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeWidget(),
    );
  }
}

class HomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HomeView("1111");
  }
}

// StatelessWidget的生命周期
class HomeView extends StatelessWidget {
  HomeView() {
    print("1---构造函数被调用");
  }

  @override
  Widget build(BuildContext context) {
    print("2---调用build方法");
    return Text("111");
  }
}
  • 打印结果
flutter: 1---构造函数被调用
flutter: 2---调用build方法
StatefulWidget生命周期
  • 生命周期图示 StatefulWidget生命周期
  • 代码演示

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeWidget(),
    );
  }
}

class HomeWidget extends StatefulWidget {
  @override
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  @override
  Widget build(BuildContext context) {
    /// Scaffold是脚手架,区分大小写
    return Scaffold(
      /// Scaffold提示区分大小写
      appBar: AppBar(
        title: Text("StatefulWidget的生命周期"),
      ),
      body: HomeView(),
      floatingActionButton: RaisedButton(
        child: Icon(Icons.add),
        /// 点击这里会让HomeView重新构建,这时就会触发HomeViewState的didUpdateWidget方法
        onPressed: () {
          setState(() {
            print("rebuid Widget");
          });
        },
      ),
    );
  }
}

// StatefulWidget的生命周期
class HomeView extends StatefulWidget {
  HomeView() {
    print("1---调用HomeView的constructor方法");
  }

  @override
  _HomeViewState createState() {
    print("2---调用HomeView的createState方法");
    return _HomeViewState();
  }
}

class _HomeViewState extends State<HomeView> {
  int _counter = 0;

  _HomeViewState() {
    print("3---调用_HomeViewState的constructor方法");
  }

  /// 执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求
  /// 这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
  /// initState上有一个注解(annotation):@mustCallSuper
  @override
  void initState() {
    // 调用: 这里是必须调用super
    final TextStyle style = TextStyle();

    super.initState();
    print("4---调用_HomeViewState的initState方法");
  }

  /// 执行didChangeDependencies方法,这个方法在两种情况下会调用
  ///   情况一:调用initState会调用;
  ///   情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("5---调用_HomeViewState的didChangeDependencies方法");
  }

  /// 执行build方法,来看一下我们当前的Widget需要渲染哪些Widget
  @override
  Widget build(BuildContext context) {
    print("6---调用_HomeViewState的build方法");
    return Column(
      children: <Widget>[
        RaisedButton(
          child: Icon(Icons.add),
          onPressed: () {
            setState(() {
              _counter++;
            });
          },
        ),
        Text("数字:$_counter")
      ],
    );
  }

  /// 当前的Widget不再使用时,会调用dispose进行销毁
  @override
  void dispose() {
    print("---调用_HomeViewState的dispose方法");
    super.dispose();
  }

  /// 执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会重新构建Widget时会调用didUpdateWidget方法
  @override
  void didUpdateWidget(HomeView oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("---didUpdateWidget");
  }
}
  • 打印结果
flutter: 1---调用HomeView的constructor方法
flutter: 2---调用HomeView的createState方法
flutter: 3---调用_HomeViewState的constructor方法
flutter: 4---调用_HomeViewState的initState方法
flutter: 5---调用_HomeViewState的didChangeDependencies方法
flutter: 6---调用_HomeViewState的build方法

// 这里是Android Studio里才会打印,VSCode里就不会,具体原因不详
flutter: 1---调用HomeView的constructor方法
flutter: ---didUpdateWidget
flutter: 6---调用_HomeViewState的build方法
  • 当点击右下角的加号按钮时,会手动执行setState方法后会打印如下结果
flutter: rebuid Widget
flutter: 1---调用HomeView的constructor方法
flutter: ---didUpdateWidget
flutter: 5---调用_HomeViewState的build方法

命令式编程 和 声明式编程

  • 命令式编程:命令式编程非常好理解,就是一步步给计算机命令,告诉它我们想做什么事情;
  • 声明式编程:声明式编程通常是描述目标的性质,你应该是什么样的,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标作出相应;
  • 目前在VueReact、包括iOS中的SwiftUI中以及Flutter目前都采用了声明式编程

相关文章

网友评论

      本文标题:Flutter学习(五)开发初体验

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