美文网首页Flutter入门
Flutter入门二: 熟悉Widget、文字样式、ListVi

Flutter入门二: 熟悉Widget、文字样式、ListVi

作者: markhetao | 来源:发表于2021-02-03 11:06 被阅读0次

    Flutter入门 学习大纲

    上一节,我们完成了Flutter的环境搭建。本节,我们开始搭建项目,简单了解Flutter及其基础组件

    1. 项目创建
      1.1 命令行创建
      1.2 Android Studio创建
    2. 熟悉工程
      2.1 简单实现
      2.2 熟悉widget
      2.3 Text
      2.4 MaterialApp
      2.5 ListView 列表视图
    3. 常用基础组件
      3.1 基础文本
      3.2 富文本
      3.3 基础容器Container

    1. 项目创建

    1.1 命令行创建

    以前不支持 驼峰写法,需要通过小写字母+下划线_命名,但是现在支持

    flutter create flutter_demo
    
    • 创建成功
    image.png
    • 按照指令,到指定文件夹运行项目
    cd FlutterDemo
    flutter run
    

    注意:

    • 如果此时未打开模拟器,会提示需要选择一个模拟器
    • 如果此时打开了多个模拟器,也会提示您选中一个模拟器运行
      image.png
    • 选中模拟器后,flutter会自动使用Xcode工具进行编译
      image.png
    • 如果需要真机调试,我们需要手动打开项目工程,去Xcode配置证书

    终端运行Flutter命令键

    Flutter run key commands.
    r Hot reload. 🔥🔥🔥 热重载(比对被修改部分,更新被修改代码)
    R Hot restart.      热重启(所有资源重新加载)
    h Repeat this help message.
    d Detach (terminate "flutter run" but leave application running).
    c Clear the screen
    q Quit (terminate the application on the device).
    s Save a screenshot to flutter.png.
    b Toggle the platform brightness setting (dark and light mode).                   (debugBrightnessOverride)
    w Dump widget hierarchy to the console.                                                      (debugDumpApp)
    t Dump rendering tree to the console.                                                 (debugDumpRenderTree)
    L Dump layer tree to the console.                                                      (debugDumpLayerTree)
    S Dump accessibility tree in traversal order.                                          (debugDumpSemantics)
    U Dump accessibility tree in inverse hit test order.                                   (debugDumpSemantics)
    i Toggle widget inspector.                                         (WidgetsApp.showWidgetInspectorOverride)
    I Toggle oversized image inversion 🖼️.                                        (debugInvertOversizedImages)
    p Toggle the display of construction lines.                                         (debugPaintSizeEnabled)
    o Simulate different operating systems.                                             (defaultTargetPlatform)
    z Toggle elevation checker.
    g Run source code generators.
    M Write SkSL shaders to a unique file in the project directory.
    v Launch DevTools.
    P Toggle performance overlay.                                           (WidgetsApp.showPerformanceOverlay)
    a Toggle timeline events for all widget build methods.
    
    • 如果使用安卓模拟器,可以在顶部控制栏配置模拟器
      image.png
    • 选中模拟器,运行:


      image.png
    • 由于个人习惯,我更喜欢Xcode开发,在终端使用命令运行。执行flutter run时,会让我们选择想使用的模拟器(后续我主要使用iPhone模拟器)。

    1.2 Android Studio创建

    • 除了使用flutter create命令创建项目,我们还可使用Android Studio创建,也可以使用VSCode创建,它们都有flutter插件
    • 啥? 你问Xcode是否可以创建?
      想想苹果谷歌的竞争者关系,就知道苹果不可能做这样的支持插件的。😂

    1.2.1 使用Android Studio创建

    image.png
    • 四种创建方式:
    image.png
    • 项目基本信息

      image.png
    • 项目唯一标识支持平台

      image.png
    • Finish后,会进行网络请求拉取资源,创建成功。
      (如果没配置镜像,拉取资源非常缓慢上一节有介绍如何配置镜像

      image.png
    • 坑点
      如果Android Studio 正在运行项目,我们command + Q强制退出。下次打开Android Studio时,会回到当时的缓存。如果缓存成功找回,会运行正常。如果缓存找不到,会导致无法运行,而且新建工程无法运行

    • 处理方式:
      删除flutter目录下cache缓存文件夹中的lockfile文件,再运行项目即可。(相当于XCodeClean操作)

    # pwd请替换为自己flutter的文件目录
    rm /pwd/flutter/bin/cache/lockfile
    

    2. 熟悉工程

    • 创建项目后,可以看到main.dart很多代码。我们最快熟悉的方式是: 全部删除手动实现分析
      image.png

    2.1 简单实现

    Flutter中,我们使用的开发语言Dart,现在我们先体验,完整的Dart语法,我们可以去官网了解

    • 手动实现:
    // 以下代码,均使用Dart语言编写
    
    // 资源包  (可以理解为我们iOS的UIKit)
    import 'package:flutter/material.dart';
    
    // main入口函数  (就像iOS main.m中的main函数)
    void main() {
      // runApp(app); // runApp就像iOS的 UIApplication, 而app就像我们的根控制器
      // flutter中没有控制器和视图的概念,都是widget组件。
      runApp(
        Center(  //我们用Center部件,自动居中展示。这是Center的构造函数
          // child就像subView
          // Text是一种文本框样式,设置默认值和显示方向(ltf: left to right 从左到右)
          child: Text('Hello', textDirection: TextDirection.ltr,),
        )
      );
    }
    
    • flutter运行,文本框成功展示
      image.png

    2.2 熟悉widget

    Widget: 作用类似于OC中的UIView,是小部件。分为两大类:

    • StatelessWidget:无状态组件。快捷键stless
      创建后决定了样式,状态更改需要手动创建新widget

    • StatefulWidget: 有状态组件。快捷键stful
      本质上也是无状态组件渲染无状态,因为UI本身无状态的。但它会保留组件状态,记录组件状态的属性改变时,会自动重新渲染。直到该组件完全销毁,才会释放记录的属性

    2.2.1 无状态组件(StatelessWidget)
    • 我们将main入口的Center组件改成自定义MyWidget无状态组件。
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
        MyWidget(), //()是构造函数,类似C++。
      );
    }
    
    // 无状态Widget快捷键是 stless
    // 一个Widget,就是一个class类
    class MyWidget extends StatelessWidget {
      @override
      // build:会将小部件放到渲染树中去
      // 渲染树: 会从main入口的第一个widget开始渲染,然后以树结构依次向下渲染组件
      // 所有widget都必须有build方法,build返回的是什么,MyWidget在外界渲染的就是什么
      Widget build(BuildContext context) {
        return Center(
          child: Text(
            'Hello Flutter2',
            textDirection: TextDirection.ltr,
          ),
        );
      }
    }
    
    • 选择模拟器debug运行,模拟器启动后,可以修改文本内容,点击热重载按钮,感受热重载的强大
    image.png
    • reload热重载功能,是通过比较新旧代码变化,来更新被改动部分代码的。 但是有些场景热重载失败的,只能restart重启才可以。

    具体场景,参考Flutter官网介绍

    Dart语言简写:

    • Dart语言中,如果函数只有一行内容时,可以使用=>缩写:
    // 改动前
    //void main() {
    //  runApp(MyWidget());
    //}
    // 简写:
    void main() => runApp(MyWidget()); 
    

    2.3 Text

    • Text是一个文本组件,是StatelessWidget不可变组件。
    2.3.1 构造函数与参数
    • Text为例,构造函数是Text(),包含必选参数可选值,可选值可以赋默认值
      image.png
    2.3.2 final和const修饰符

    finalconst都类似于Swiftlet,是不可变的。

    • final可以不赋初始值,运行时赋值
    • const必须在创建时就赋值

    比如Text使用频率最高styletextAlign,都是final声明,因为Text本身是StatelessWidget不可变的组件。

    image.png
    • Text加入样式,使用变量创建TextStyle_下划线表示私有变量
    import 'package:flutter/material.dart';
    
    // 入口,展示MyWidget组件
    void main() => runApp(MyWidget());
    
    class MyWidget extends StatelessWidget {
      @override
      // build 确定组件返回的内容
      Widget build(BuildContext context) {
    
        // final创建一个_textStyle不可变变量,_开头的属性,表示私有属性
        final _textStyle = TextStyle(color: Colors.red, fontSize: 40.0, fontWeight: FontWeight.bold);
    
        return Center(
          child: Text(
            'Hello Flutter',
            textDirection: TextDirection.ltr,
            style: _textStyle, // 直接使用变量
          ),
        );
      }
    }
    

    TextTextStyle相关属性参数,可以Command + 鼠标左键查看

    image.png

    2.4 MaterialApp

    • MaterialApp:Flutter推荐方式,提供快速构建APP的方式(包括导航栏内容主题等)
    • home: 类似于根控制器,需要指定一个widget
      Scaffold: 类似于导航控制器NavigationController,包含导航栏相关属性。是一个可变组件statefulWidget
      appBar:导航栏
      body: 内容
    • theme主题,可控制主题色
    import 'package:flutter/material.dart';
    
    // 入口,展示MyWidget组件
    void main() => runApp(App());
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // MaterialApp:Flutter推荐方式,提供快速构建APP的方式(包括导航栏、内容、主题等)
        return MaterialApp(
          // home: 类似于根控制器,也是需要指定一个widget
          // Scaffold: 类似于导航控制器NavigationController,包含导航栏相关属性。是一个可变组件statefulWidget
          home: Scaffold(
            // appBar:导航栏
            appBar: AppBar(
                  title: Text('Flutter Demo'),
                ),
            // body: 内容
            body: MyWidget(),
          ),
          // theme:主题,可控制主题色
          theme: ThemeData(
            primaryColor: Colors.yellow
          ),
        );
      }
    }
    
    class MyWidget extends StatelessWidget {
      @override
      // build 确定组件返回的内容
      Widget build(BuildContext context) {
    
        // final创建一个_textStyle不可变变量,_开头的属性,表示私有属性
        final _textStyle = TextStyle(color: Colors.red, fontSize: 40.0, fontWeight: FontWeight.bold);
    
        return Center(
          child: Text(
            'Hello Flutter',
            textDirection: TextDirection.ltr,
            style: _textStyle, // 直接使用变量
          ),
        );
      }
    }
    
    image.png

    2.5 ListView 列表视图

    • 列表视图:类似iOS的UITableView,但是没有Section的概念。
    2.5.1 准备Model

    在布局之前,先准备数据

    • 新建Model文件夹,新建car.dart文件:
      image.png
    // 不需要导入material.dart,因为Car直接继承Object
    
    // Car 模型
    class Car {
      // 构造函数: {}内是可选值
      const Car({
        this.name,
        this.imageUrl,
      });
    
      // 名称
      final String name;
      // 图片链接
      final String imageUrl;
    }
    
    final List<Car> datas = [
      Car(
        name: '保时捷918 Spyder',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/7d8be6ebc4c7c95b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      ),
      Car(
        name: '兰博基尼Aventador',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/e3bfd824f30afaac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      ),
      Car(
        name: '法拉利Enzo',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/a1d64cf5da2d9d99?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      ),
      Car(
        name: 'Zenvo ST1',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/bf883b46690f93ce?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      ),
      Car(
        name: '迈凯伦F1',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/5a7b5550a19b8342?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      ),
      Car(
        name: '萨林S7',
        imageUrl:
        'https://img.haomeiwen.com/i2990730/2e128d18144ad5b8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
      )
    ];
    
    2.5.2 设置ListView
    • 如果直接导入Car模型,需要导入头文件

    三种导入头文件的方式

    1. 直接手写,在顶部import
    2. 左键点击Car,出现红色小灯泡,点击import Library
    3. 鼠标光标放在Car上,按住Option+Enter键,再按一次Enter键,可快捷导入import Library

    final List<Car> datas = []

    • Container: 类似于htmldiv,也类似于iOSUIView,就是用来放东西的。需要设置大小(也可以被子控件撑出大小)
    • Column: 内容垂直排列的容器
    • row: 内容水平排列的容器
    • stack: 内容重叠的容器
    • Image图片可变组件network加载网络图片
    • SizedBox: 空容器,有大小。(有时为了便于内部插入元素,会直接使用Container
    • MaterialAppdebugShowCheckedModeBanner隐藏导航栏Debug角标
    import 'package:flutter/material.dart';
    
    import 'Model/car.dart';
    
    // 入口,展示MyWidget组件
    void main() => runApp(App());
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // MaterialApp:Flutter推荐方式,提供快速构建APP的方式(包括导航栏、内容、主题等)
        return MaterialApp(
          // 隐藏导航栏Debug角标
          debugShowCheckedModeBanner: false,
          // home: 类似于根控制器,需要指定一个widget
          home: Home(),
          // theme:主题,可控制主题色
          theme: ThemeData(
            primaryColor: Colors.yellow
          ),
        );
      }
    }
    
    // Home 组件
    class Home extends StatelessWidget {
    
      // 回调函数,返回widget组件
      Widget _cellForRow(BuildContext context, int index) {
        // Container类似于html的div,也类似于iOS的UIView,就是用来放东西的
        // 需要大小(也可以被子控件撑出大小)
        return Container(
            color: Colors.white,
            // height: 20,
            margin:  EdgeInsets.all(10), //EdgeInsets.only(top: 1),
            // 子控件
            // Column 内容垂直排列的容器 row 内容水平排列的容器 stack 内容重叠的容器
            // Image图片可变组件,network加载网络图片
            child: Column(
              children: <Widget>[
                Image.network(datas[index].imageUrl),
                SizedBox(height: 8,),
                Text(
                  datas[index].name,
                  style: TextStyle(
                      fontWeight: FontWeight.bold,
                    fontSize: 18.0,
                    fontStyle: FontStyle.italic),),
                SizedBox(height: 8,)
              ]
            )
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey[100],
          appBar: AppBar(
            title: Text("Flutter Demo"),
          ),
          // ListView 列表组件(没有iOS的Section概念)
          body: ListView.builder(
            // cell个数
            itemCount: datas.length,
            // cell内容(等同与cellForRow)build 是渲染
            // iOS中是使用代理和协议完成,这里是直接使用回调函数,有两个入参
            itemBuilder: _cellForRow,
          ),
        );
      }
    }
    
    // 模型数组
    // 没导入头文件时,会提示需要导入Car头文件
    // 三种导入头文件的方式
    // 1. 直接手写,在顶部import
    // 2. 左键点击Car,出现红色小灯泡,点击import Library
    // 3. 鼠标光标放在Car上,按住Option+Enter键,再按一次Enter键,可快捷导入import Library
    // final List<Car> datas = []
    
    • 效果展示:


      image.png
    • 实际开发中,我们可以将ListView内容抽离出来,做成单独文件listView_demo.dart:

    import 'package:flutter/material.dart';
    
    import 'car.dart';
    
    class ListViewDemo extends StatelessWidget {
    
      // 回调函数,返回widget组件
      Widget _cellForRow(BuildContext context, int index) {
        return Container(
            color: Colors.white,
            margin:  EdgeInsets.all(10)
            child: Column(
                children: <Widget>[
                  Image.network(datas[index].imageUrl),
                  SizedBox(height: 8,),
                  Text(
                    datas[index].name,
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 18.0,
                        fontStyle: FontStyle.italic),),
                  SizedBox(height: 8,)
                ]
            )
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return ListView.builder(
          // cell个数
          itemCount: datas.length,
          itemBuilder: _cellForRow,
        );
      }
    }
    
    • main.dart中,直接使用我们封装的ListViewDemo导入头文件)即可:
    import 'package:flutter/material.dart';
    
    import 'Model/listView_demo.dart';
    
    // 入口,展示MyWidget组件
    void main() => runApp(App());
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // MaterialApp:Flutter推荐方式,提供快速构建APP的方式(包括导航栏、内容、主题等)
        return MaterialApp(
          // 隐藏导航栏Debug角标
          debugShowCheckedModeBanner: false,
          // 根组件
          home: Home(),
          // 主题
          theme: ThemeData(
            primaryColor: Colors.yellow
          ),
        );
      }
    }
    
    // Home 组件
    class Home extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey[100],
          appBar: AppBar(
            title: Text("Flutter Demo"),
          ),
          // 列表
          body: ListViewDemo(),
        );
      }
    }
    

    3. 常用基础组件

    • 上面讲了简单封装,我们现在对几个基础组件进行简单封装

    3.1 基础文本

    • 新建一个base_widget.dart文件,创建TextDemo组件:
    1. 通过属性声明变量内容样式),_开头的属性为私有属性
    2. $ + 属性名快捷插入变量内容。 如果后面有其他字符等信息,可使用{}包裹起来。
    3. Text可设置maxLines最大行数,超出部分样式通过overflow设置(ellipsis尾部省略号
    import 'package:flutter/material.dart';
    
    //【普通文本 Demo】
    class TextDemo extends StatelessWidget {
      // 文本样式(私有属性)
      final TextStyle _textStyle = TextStyle(
        fontSize: 24.0,
      );
    
      final String _title = 'Flutter入门';
      final String _author = 'HT';
    
      @override
      Widget build(BuildContext context) {
    
        // $ + 属性名: 快捷插入变量内容。 如果后面有其他字符等信息,可使用{}包裹起来。
        return Text(
          '《$_title》这是一个TextDemo,使用Flutter开发。由iOS开发者${_author}开发,快速配置,简易上手的零基础学习方式。欢迎阅读和上手练习,不懂之处,留言交流',
          textAlign: TextAlign.center,
          style: _textStyle,
          maxLines: 4, // 最多4行
          overflow: TextOverflow.ellipsis, // 超出显示省略号
        );
      }
    }
    
    • main.dart中指定bodyTextDemo:
      image.png
    • 展示样式:


      image.png

    3.2 富文本

    • 使用RichText组件,通过给text指定TextSpan类型,添加children:<TextSpan>数组,数组内创建TextSpan,并赋值样式即可:
    import 'package:flutter/material.dart';
    
    //【富文本 Demo】
    class RichTextDemo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RichText(
          //基础元素
          text: TextSpan(
              text: '《Flutter 入门》',
              style: TextStyle(
                fontSize: 30,
                color: Colors.black,
              ) ,
              // 子元素
              children:<TextSpan>[
                // 元素一
                TextSpan(
                  text: 'HT',
                  style: TextStyle(
                    fontSize: 20,
                    color: Colors.blue,
                  ),
                ),
                // 元素二
                TextSpan(
                  text: '666',
                  style: TextStyle(
                    fontSize: 40,
                    color: Colors.red,
                  ),
                ),
              ]),
        );
      }
    }
    
    • main.dart中指定bodyRichTextDemo:
      image.png
    • 展示样式:


      image.png

    3.3 基础容器Container

    • 创建基础组件BaseWidgetDemo,返回Container组件:
    //【基础组件Demo】
    class BaseWidgetDemo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // 容器背景蓝色(没有给定大小时,根据子视图大小撑开)
        return Container(
          color: Colors.blue,
          // 子视图横向布局
          child: Row(
            children: <Widget>[
              Container(
                // 内边距
                padding: EdgeInsets.only(left: 10,top: 10,right: 10,bottom: 10),
                // 外边距
                margin: EdgeInsets.only(left: 10,top: 10,right: 10,bottom: 10),
                // 子元素红色
                color: Colors.red,
                // 子元素内容包含➕号图片
                child: Icon(Icons.add),
              ),
              Container(
                // 子元素红色
                color: Colors.red,
                // 子元素内容包含➕号图片
                child: Icon(Icons.add),
              ),
            ],
          ),
        );
      }
    }
    
    image.png
    • 展示样式:


      image.png
    • Dart中没有iOSButton概念:
      因为Button实际是图片 + 文字 + 手势 + 状态组成,以及封装相应的响应事件。我们一般使用手势小部件,包装图片文字即可。

    【快捷键】
    command + option + L:自动格式化
    command + -:折叠当前函数
    command + shift + -:折叠全部函数
    command + {:回到上一步
    command + }: 回到下一步
    command + shift + O: 快速到指定文件
    stless:不可变组件
    stful: 可变组件


    本节主要是Flutter基础体验,通过本节,其实我们已经感受到了Flutter便捷
    下一节,Flutter入门三:自动布局(Row/Column/Stack)与两种Widget

    相关文章

      网友评论

        本文标题:Flutter入门二: 熟悉Widget、文字样式、ListVi

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