Flutter 学习10:NestedScrollView、SliverAppBar、TabBar

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制
  10. Flutter 学习10:NestedScrollView、SliverAppBar、TabBar

前面说过Flutter提供了太多各种各样的Widget,给开发者带来了非常棒的开发体验,很多功能和效果只要拿官方提供的Widget来用就行了。 今天说的这几个Widget如果是Android开发者应该是比较熟悉了,在Android开发中应该会经常用到。

NestedScrollView、SliverAppBar

这两个Widget要放一起说,因为NestedScrollView其实跟Android中的一样,它是一个ScrollView,但是在Android中它可以和Toolbar配合进行交互,可以做出很多效果,比如折叠Toolbar、页面滚动的时候隐藏Toolbar等效果。在Flutter里面也是一样,不过这里用SliverAppBar来代替Toolbar的位置。

Read more »

Flutter 学习9:Positioned、Transform等控件使用以及手势控制

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

上次说了一个BottomSheet的控件,非常实用的一个弹出控件。Flutter提供了很多有意思的控件,好好利用这些控件就能搭建出任何你想要的效果。

Positioned

看这个名字就大概能了解到这个是定位用的,但是它不是随便哪里都能定位的,它必须配合Stack控件用。它通过 [left], [right], [width],[top], [bottom], [height] 这六个属性将Stack的某个子控件定位到特定的位置上。

    Stack(
        children: <Widget>[
          Positioned(
            top: 150,
            left: 30,
            child: Container(
              color: Colors.red,
              width: 200,
              height: 200,
            ),
          ),
          Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Container(
              color: Colors.teal,
            ),
          )
        ],
      )

Screen Shot 2019-01-31 at 14.14.28-w417

Transform

Transform已经有写好的三种用法分别是scale、rotate、translate 。用起来非常方便,一个个来看看怎么用

Transform.scale

它就一个参数scale。在上面代码的基础上,我们把那个红色方块用Transform.scale包起来就可以放大缩小了。

var myScale = 1.0;
void _scale(bool plus) {
    if(plus) {
      myScale += 0.1;
    }else {
      myScale -= 0.1;
    }
    setState(() {});
  }
  ...
    Stack(
        children: <Widget>[
          Positioned(
            top: 150,
            left: 30,
            child: Transform.scale(
              scale: myScale,
              child: Container(
              color: Colors.red,
              width: 200,
              height: 200,
            ),
            ),
          ),
          Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Container(
              color: Colors.teal,
            ),
          ),
          Positioned(
            bottom: 20,
            right: 40,
            child: Container(
              width: 200,
              height: 64,
              child: Row(
                children: <Widget>[
                  FlatButton(
                    child: Icon(Icons.add_circle),
                    onPressed: () => _scale(true),
                  ),
                  FlatButton(
                    onPressed:  () => _scale(false),
                    child: Icon(Icons.remove_circle),
                  )
                ],
              ),
            ),
          )
        ],
      )

Transform.2019-01-31 15_16_14

Transform.rotate

它是用来旋转一定的角度用的,也只有一个参数angle。

    Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Transform.rotate(
              angle: math.pi / 2, //90度
              child: Container(
                color: Colors.teal,
                child: Center(child: Text('中国', style: TextStyle(color: Colors.white),)),
              ),
            ),
          )

Screen Shot 2019-01-31 at 15.38.26-w417

Transform.translate

最后一个是变化位置,有点类似Positioned的,就是x轴y轴的移动。

    Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Transform.translate(
              offset: Offset(130, 0),//x轴方向移动130
              child: Container(
                color: Colors.teal,
                child: Center(child: Text('中国', style: TextStyle(color: Colors.white),)),
              ),
            ),
          )

Screen Shot 2019-01-31 at 15.51.17-w417

手势控制

做移动开发的看到这里其实很容易就能想到我们经常用到的图片查看器,图片可以用双指放大缩小,单指移动位置。 那用上面两个控件结合手势控制的GestureDetector是否就可以实现图片查看器的功能了呢,试试!

网上随便找了一个小姐姐的图片,我随便找你们随便看!放大缩小移动的效果如下:

ScreenRecording_01-31-2019 17-30-23.2019-01-31 17_33_36

下面是实现的代码:

class _MyHomePageState extends State<MyHomePage> {
  var myScale = 1.0;
  var myPosition = Offset(0.0, 0.0);
  var mySize = Size(0.0, 0.0);
  var lastPosition = Offset(0.0, 0.0);
  var startMovePosition = Offset(0.0, 0.0);
  var edgePosition = Offset(0.0, 0.0);
  int numPointers = 0;
  var lastScale = 1.0; // 多次放大缩小的时候保存上一次的结果。
  var scaling = false;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    mySize = size * myScale;
    final dx = -(mySize.width - size.width);
    final dy = -(mySize.height - size.height);
    edgePosition = Offset(dx, dy);
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Listener(
            onPointerDown: (_) => numPointers++,
            onPointerUp: (_) => numPointers--,
            child: GestureDetector(
              child: Stack(
                children: <Widget>[
                  Positioned(
                    top: myPosition.dy,
                    left: myPosition.dx,
                    width: mySize.width,
                    height: mySize.height,
                    child: Transform.scale(
                      scale: myScale,
                      child: Image.network(
                        'http://img.muliba.net/post/timg.jpeg',
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                ],
              ),
              onScaleStart: _scaleStart,
              onScaleEnd: _scaleEnd,
              onScaleUpdate: _scaleUpdate,
            )));
  }

  void _scaleStart(ScaleStartDetails detail) {
    if (numPointers == 1 && !scaling) {
    lastPosition = myPosition; //标记下位置
    startMovePosition = detail.focalPoint;
    }
    if (numPointers == 2) {
      if (!scaling) {
        scaling = true;
      }
    }
  }

  void _scaleUpdate(ScaleUpdateDetails detail) {
    if (numPointers == 1 && !scaling) {
    final distance = detail.focalPoint - startMovePosition; // 移动距离
    final newPosition = lastPosition + distance; //移动
    var x = newPosition.dx;
    if (x > 0) {
      x = 0.0;
    }
    if (x < edgePosition.dx) {
      x = edgePosition.dx;
    }
    var y = newPosition.dy;
    if (y > 0) {
      y = 0.0;
    } else if (y < edgePosition.dy) {
      y = edgePosition.dy;
    }
    myPosition = Offset(x, y); //移动
    setState(() {});
    }
    if (numPointers == 2) {
      if (scaling) {
        var newScale = lastScale * detail.scale;
        if (newScale < 1.0) {
          newScale = 1.0;
        } else if (newScale > 5.0) {
          newScale = 5.0;
        }
        myScale = newScale;
        // 计算大小
        setState(() {});
      }
    }
  }

  void _scaleEnd(ScaleEndDetails detail) {
    if (scaling) {
      lastScale = myScale;
      scaling = false;
    }
  }
}

是不是很方便实用,Flutter确实让开发越来越简单了!👍

Read more »

Flutter 学习8:BottomSheet

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

BottomSheet 是一个Flutter提供的一个组件,有点类似Android的PopWindow,从底部弹出一个Widget。今天就说说这个BottomSheet的用法。

首先这个BottomSheet,Flutter提供了两种方式显示,形式有点不同,一个是ModalBottomSheet, 一个是PersistentBottomSheet。

ModalBottomSheet

这个ModalBottomSheet就是类似一个Dialog,有一个半透明的背景层,然后上面显示你自定义的内容。 用法非常简单,Flutter提供了一个showModalBottomSheet的方法弹出一个BottomSheet。

showModalBottomSheet(
        context: context,
        builder: (context) {
          return Container(
            height: 300,
            color: Colors.greenAccent,
            child: Center(
              child: Text('ModalBottomSheet'),
            )
          );
        })

只要在空白处点击就能关闭这个窗口,而且他这个方法是让你生成一个WidgetBuilder对象,你完全可以自定义任何内容在里面,非常方便实用。

Read more »

Flutter 学习7:Dart语言基础

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

这些概念其实一开始就应该去弄明白的,当时接触Flutter的时候发现跟Java很类似,也就没有花时间去看看文档,后来在用的过程中发现还是有必要看看文档的,不然在开发中经常会遇到很多语法上面的疑惑再去查文档,蛮浪费时间的。

关键概念

 Object s = 'this is string';
  s = 8;
  print(s.toString()); // 这里s是Object对象,只能执行Object的函数
  dynamic s = 'this is string';
  if (s is String) {
    print(s); // 打印this is string
  }
  s = 8;
  if (s is int) {
    s++; // 自动推断是int类型
    print(s.toString()); // 打印9
  }

这里的is关键字就是判断变量类型的,并且还有智能推断作用,只要通过了判断,if内部的语句中这个变量类型就是对应的判断成功的类型。

变量

和其他语言一样Dart定义了很多常见的基本变量类型,int、double、String、bool等,其中int、double都是64位的。但前面说了Dart是有类型推断的,所以一般变量可以用var关键字表示。还有常量可以用finalconst表示,他们两区别是final是运行时常量,const是编译时常量。

Read more »

Flutter 学习6:绘制动画

这个是我学习Flutter的一个系列文章:

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

前面文章写了关于Flutter插件的开发,最近写了一个仿照微信拍照功能的的开发包。中间那个拍照按钮有一个在拍摄视频的时候有一个动画效果。于是就学习了下关于Flutter动画绘制的过程。

绘制图形

在Flutter中关于绘图有一个专门的类:CustomPainter 。它是一个抽象类,需要实现两个方法:

@override
  void paint(Canvas canvas, Size size) {
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return null;
  }

Read more »

Appium 自动化测试工具使用

Appium是一个开源的自动化测试工具,它支持IOS平台和Android平台的原生应用、web应用和混合应用。最重要的一点是它是跨平台的,它允许只用一套API的自动化测试代码就能实现跨平台(IOS、Android)的测试,大大增加了代码的复用性。

Appium核心就是一个web服务器,它有一个服务端、客户端。服务端提供了一套RESTful的接口,它接收来自客户端的命令,根据接收到的命令在移动设备上执行,然后讲执行的结果反馈给客户端。

环境准备

在开始之前需要准备一些基本的环境。JDK、Android SDK,这两个是必须的,把环境变量配置好。如果使用npm安装还需要准备Nodejs环境。 还有就是测试的移动设备,要连接到安装Appium服务端的电脑上。然后使用adb devices命令测试下是否连接好了。

Read more »

Flutter 学习5:开发Dart包和插件包

这个是我学习Flutter的一个系列文章:

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

在上面的列表中第二篇文章就说到过怎么使用一个外部的包,需要到pubspec.yaml的dependencies中引入,然后到需要使用的Dart文件中import。外部包这种东西在开发中是很常见的了,它有可能是某种需求的UI库,也有可能是方便开发的工具库等等。

在Flutter中这中外部包分两种类型,一种叫Dart Packages ,它是纯Dart开发的,不涉及Native层的一些开发工具包或者UI包,比如前面用到english_words。还有一种叫Plugin Packages,它是涉及到Native层的,需要调用Native的原生代码的,可能是要调用硬件,比如获取电池电量等类似的功能库的。一个外部包至少包含一个pubspec.yaml(声明一些包名称、版本、作者等数据的文件),一个lib文件夹(包含公开的源代码,最少应该要有一个包名称命名的dart文件)。

前面的文章已经提到了,这些外部包有个官方的包管理的网站 ,现在已经有很多可用的包了,如果开发中想要用什么包,可以先去上面找找,有的话直接拿来用就不用重复造轮子了。当然肯定会有不满足需求的时候,那我们得学会自己开发!😊

Read more »

Flutter 学习4:集成到原有的项目中

这个是我学习Flutter的一个系列文章:

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

从前面的学习中,我觉得如果用Flutter开发一个全新的App应该还是问题不大的。它的优势在于UI统一,可以用一套UI适用两端,节省开发成本。但是也有问题是毕竟是新的多端融合方案,还有很多大环境的不支持,比如第三方SDK如何集成等问题。 如果是已经有的项目呢,能够集成Flutter吗?答案是肯定的!官方有个wiki就是介绍这个的。

IOS

创建一个Flutter模块

首先要把Flutter模块建起来,按照下面的命令来创建模块:

$ cd some/path/
$ flutter create -t module ff_flutter

这里的some path就是你原来项目的父目录,敲完命令后的目录结构大概是这样的:

some/path/
    MyOldApp/
        MyOldApp/
                AppDelegate.swift
    ff_flutter/
        lib/main.dart
        .ios/

Read more »

Flutter 学习3:转场、导航

这个是我学习Flutter的一个系列文章:

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

上次文章从main.dart文件开始分析,并做出来一个ListView的页面。我跟着官方的例子继续往下看,它写了一个功能,当前的列表数据可以多选,然后可以跳转到一个新的页面查看选中的值。这里就涉及到了页面跳转。具体来看看怎么做。

示例的页面跳转

前面列表页面已经出来,那要多选需要把选中的数据保留下来。

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  final _saved = new Set<WordPair>(); //保存选中的值
  ...
  }

然后列表的Item我们添加了一个爱心,用来表示是否选中。这里我又要👍一个,Flutter自带的Material主题提供了各种Icon,可以直接拿来用,非常方便!

Widget _buildItem(WordPair pair) {
    //这里定义当前项是否已经选中
    final alreadySaved = _saved.contains(pair);
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
        ),
        //这里就是爱心图标,红色表示选中,空心表示未选中
      trailing: Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      //ListTile的点击事件 选中还是取消选中
      onTap: (){
        setState(() {
                  if (alreadySaved) {
                    _saved.remove(pair);
                  }else {
                    _saved.add(pair);
                  } 
                });
      },
    );
  }

Read more »

Flutter 学习:从main.dart文件说起

这个是我学习Flutter的一个系列文章:

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制

flutter的命令

上次那篇文章写了环境安装,开发工具插件安装然后用插件生成一个flutter项目。生成一个新的项目非常简单,点一下插件的New Project就行了。其实点击这个New Project就是执行了一个命令:

$ flutter create appName
# 这个appName就是你要生成的项目名

这个时候生成的项目里面,android项目用的是默认的java语言,IOS项目用的是Object-C。如果你比较新潮用了kotlin、swift语言。那只需要在那个命令后面加个参数就行了。

$ flutter create -i swift -a kotlin appName

Read more »