一个Android菜鸟入门Flutter 笔记(一)

2020-04-16  本文已影响0人  潇风寒月

1. Dart 基础语法

1.1 hello world

先来看个hello world,入口依然是main方法.

printInteger(int a) {
  print('Hello world, this is $a.'); 
}

main() {
  var number = 2019; 
  printInteger(number); 
}

1.2 变量与类型

var a = 1;
int b = 1;
num c = 3;

int x = 1;
int hex = 0xEEADBEEF;
double y = 1.1;
double exponents = 1.13e5;
int roundY = y.round();

//List
var arr1 = ["Tom", "Andy", "Jack"];
var arr3 = <String>["Tom", "Andy", "Jack"];
var arr2 = List.of([1, 2, 3]);
var arr4 = List<int>.of([1, 2, 3]);
arr2.add(499);
arr2.forEach((v) => print(v));

//Map
var map1 = {"name": "Tom", "sex": "male"};
var map2 = new Map();
//添加或修改
map2["name"] = "Tom";
map2["name"] = "Tom22";
map2["sex"] = "male";

print(map2);
map2.forEach((k, v) => print('k = $k v=$v'));

1.3 函数

void main() {
  Function f = isZero;
  int x = 10;
  int y = 10;
  printInfo(x, f);
  printInfo(y, f);

  enable1Flags(bold: true);
}

bool isZero(int a) {
  return a == 0;
}

bool isNull(var a) => a == null;

void printInfo(int number, Function check) {
  print('$number is zero: ${check(number)}');
}

//可选命名参数   Flutter 中大量使用
void enable1Flags({bool bold, bool hidden}) => print("$bold $hidden");

//可选命名参数 加默认值
void enable2Flags({bool bold = true, bool hidden = false}) =>
    print('$bold $hidden');

//可忽略参数,也可以加默认值
void enable3Flags(bool bold, [bool hidden]) => print("$bold $hidden");

//返回值类型 省略
price() {
    double sum = 0.0;
    for (var i in booking) {
      sum += i.price;
    }
    return sum;
}

1.4 类

void main() {
  //类 都是继承自Object
  //无修饰符关键字 变量与方法前面加"_"则表示private,不加则为public
  //加"_"的限制范围并不是类访问级别的,而是库访问级别的
  Test test = Test();
  print(test.b);

  var p = Point(1, 2);
  p.printInfo();
  Point.factor = 10;
  Point.printZValue();
  //为空时跳过执行
  Point?.printZValue();

  var p2 = Point2.test(1);
}

class Test {
  int _a = 1;
  int b = 2;
}

class Point {
  num x, y;
  static num factor = 0;

  //语法糖,等同于在函数体内:this.x = x;this.y = y;
  Point(this.x, this.y);

  void printInfo() => print('($x,$y)');

  static void printZValue() => print('factor=$factor');
}

class Point2 {
  num x, y, z;

  //z也得到了初始化
  Point2(this.x, this.y) : z = 0;

  //重定向构造函数
  Point2.test(num x) : this(x, 0);
}

class Point3 {
  num x = 0, y = 0;

  void printInfo() => print('($x,$y)');
}

class Vector extends Point3 {
  num z = 0;

  //覆写了父类的方法
  @override
  void printInfo() => print('x = $x,y=$y');
}

class Coordinate implements Point3 {
  //成员变量需要重新声明
  num x = 0, y = 0;

  //成员函数需要重新实现
  @override
  void printInfo() {}
}

//混入Mixin  可以视为具有实现方法的接口
//非继承的方法,使用其他类中的变量与方法
class Coordinate2 with Point3 {}

2. Flutter区别于其他方案的关键技术

3. Widget的设计思路和基本原理

4. State的选择

5. 生命周期

5.1 Widget 视图生命周期

方法名 功能 调用时机 调用次数
构造方法 接收父Widget传递的初始化UI配置数据 创建State时 1
initState 与渲染相关的初始化工作 在State被插入视图树时 1
didChangeDependencies 处理State对象依赖关系变化 initState后及State对象依赖关系变化时 >=1
build 构建视图 State准备好数据需要渲染时 >=1
setState 触发视图重建 需要刷新UI时 >=1
didUpdateWidget 处理Widget的配置变化 父Widget setState触发子Widget重建时 >=1
deactivate 组件被移除 组件不可视 >=1
dispose 组件被销毁 组件被永久移除 1

5.2 App(也是Widget) 生命周期

abstract class WidgetsBindingObserver {
  //页面pop
  Future<bool> didPopRoute() => Future<bool>.value(false);
  //页面push
  Future<bool> didPushRoute(String route) => Future<bool>.value(false);
  //系统窗口相关改变回调,如旋转
  void didChangeMetrics() { }
  //文本缩放系数变化
  void didChangeTextScaleFactor() { }
  //系统亮度变化
  void didChangePlatformBrightness() { }
  //本地化语言变化
  void didChangeLocales(List<Locale> locale) { }
  //App生命周期变化
  void didChangeAppLifecycleState(AppLifecycleState state) { }
  //内存警告回调
  void didHaveMemoryPressure() { }
  //Accessibility相关特性回调
  void didChangeAccessibilityFeatures() {}
}
class _MyHomePageState extends State<MyHomePage>  with WidgetsBindingObserver{
...
  @override
  @mustCallSuper
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);//注册监听器
  }
  @override
  @mustCallSuper
  void dispose(){
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);//移除监听器
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    print("$state");
    if (state == AppLifecycleState.resumed) {
      //do sth
    }
  }
}

WidgetsBinding.instance.addPostFrameCallback((_){
    print("单次Frame绘制回调");//只回调一次
  });
WidgetsBinding.instance.addPersistentFrameCallback((_){
  print("实时Frame绘制回调");//每帧都回调
});

6. 文本

Text(
  '文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如Android里的TextView,或是iOS中的UILabel。',
  textAlign: TextAlign.center,//居中显示
  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red),//20号红色粗体展示
);

TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); //黑色样式

TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); //红色样式

Text.rich(
    TextSpan(
        children: <TextSpan>[
          TextSpan(text:'文本是视图系统中常见的控件,它用来显示一段特定样式的字符串,类似', style: redStyle), //第1个片段,红色样式 
          TextSpan(text:'Android', style: blackStyle), //第1个片段,黑色样式 
          TextSpan(text:'中的', style:redStyle), //第1个片段,红色样式 
          TextSpan(text:'TextView', style: blackStyle) //第1个片段,黑色样式 
        ]),
  textAlign: TextAlign.center,
);

7. 图片


FadeInImage.assetNetwork(
  placeholder: 'assets/loading.gif', //gif占位
  image: 'https://xxx/xxx/xxx.jpg',
  fit: BoxFit.cover, //图片拉伸模式
  width: 200,
  height: 200,
)

8. 按钮

FloatingActionButton(onPressed: () => print('FloatingActionButton pressed'),child: Text('Btn'),);
FlatButton(onPressed: () => print('FlatButton pressed'),child: Text('Btn'),);
RaisedButton(onPressed: () => print('RaisedButton pressed'),child: Text('Btn'),);
FlatButton(
    color: Colors.yellow, //设置背景色为黄色
    shape:BeveledRectangleBorder(borderRadius: BorderRadius.circular(20.0)), //设置斜角矩形边框
    colorBrightness: Brightness.light, //确保文字按钮为深色
    onPressed: () => print('FlatButton pressed'), 
    child: Row(children: <Widget>[Icon(Icons.add), Text("Add")],)
);

9. ListView

9.1 ListView

构造函数名 特点 适用场景 适用频次
ListView 一次性创建好全部子Widget 适用于展示少量连续子Widget的场景
ListView.builder 提供子Widget创建方法,仅在需要展示的时候才创建 适用于子Widget较多,且视觉效果呈现某种规律性的场景
ListView.separated 与ListView.builder类似,并提供了自定义分割线的功能 与ListView.builder场景类似
ListView(
  children: <Widget>[
    //设置ListTile组件的标题与图标 
    ListTile(leading: Icon(Icons.map),  title: Text('Map')),
    ListTile(leading: Icon(Icons.mail), title: Text('Mail')),
    ListTile(leading: Icon(Icons.message), title: Text('Message')),
  ]);
ListView.builder(
    //itemCount,表示列表项的数量,如果为空,则表示 ListView 为无限列表
    itemCount: 100, //元素个数
    itemExtent: 50.0, //列表项高度
    itemBuilder: (BuildContext context, int index) => ListTile(title: Text("title $index"), subtitle: Text("body $index"))
);
//使用ListView.separated设置分割线
ListView.separated(
    itemCount: 100,
    separatorBuilder: (BuildContext context, int index) => index %2 ==0? Divider(color: Colors.green) : Divider(color: Colors.red),//index为偶数,创建绿色分割线;index为奇数,则创建红色分割线
    itemBuilder: (BuildContext context, int index) => ListTile(title: Text("title $index"), subtitle: Text("body $index"))//创建子Widget
)

9.2 CustomScrollView


CustomScrollView(
  slivers: <Widget>[
    SliverAppBar(//SliverAppBar作为头图控件
      title: Text('CustomScrollView Demo'),//标题
      floating: true,//设置悬浮样式
      flexibleSpace: Image.network("https://xx.jpg",fit:BoxFit.cover),//设置悬浮头图背景
      expandedHeight: 300,//头图控件高度
    ),
    SliverList(//SliverList作为列表控件
      delegate: SliverChildBuilderDelegate(
            (context, index) => ListTile(title: Text('Item #$index')),//列表项创建方法
        childCount: 100,//列表元素个数
      ),
    ),
  ]);

9.3 ScrollController

class MyControllerAppState extends State<MyControllerApp> {
  //ListView控制器
  ScrollController _controller;
  //标识目前是否需要启用top按钮
  bool isToTop = false;

  @override
  void initState() {
    _controller = ScrollController();
    _controller.addListener(() {
      //ListView向下滚动1000 则启用top按钮
      if (_controller.offset > 1000) {
        setState(() {
          isToTop = true;
        });
      } else if (_controller.offset < 300) {
        //向下滚动不足300,则禁用按钮
        setState(() {
          isToTop = false;
        });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: ListView.builder(
            //将控制器传入
            controller: _controller,
            itemCount: 100,
            itemExtent: 100,
            itemBuilder: (context, index) =>
                ListTile(title: Text('index $index'))),
        floatingActionButton: RaisedButton(
          //如果isToTop是true则滑动到顶部,否则禁用按钮
          onPressed: isToTop
              ? () {
                  //滑动到顶部
                  _controller.animateTo(0.0,
                      duration: Duration(microseconds: 200),
                      curve: Curves.ease);
                }
              : null,
          child: Text('top'),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

}

9.4 NotificationListener

class MyListenerApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: NotificationListener<ScrollNotification>(  
          //添加NotificationListener作为父容器
          //注册通知回调
          onNotification: (scrollNotification) {
            //开始滑动
            if (scrollNotification is ScrollStartNotification) {
              //scrollNotification.metrics.pixels 滑动的位置
              print('scroll start ${scrollNotification.metrics.pixels}');
            } else if (scrollNotification is ScrollUpdateNotification) {
              //滑动中
              print('scroll update');
            } else if (scrollNotification is ScrollEndNotification) {
              //滑动结束
              print('scroll end');
            }
            return null;
          },
          child: ListView.builder(
              itemCount: 100,
              itemExtent: 70,
              itemBuilder: (context, index) => ListTile(
                    title: Text('index $index'),
                  )),
        ),
      ),
    );
  }
}

10. 布局容器

10.1 Container,Padding,Center

getContainer() {
return Container(
  child: Center(
    child: Text('Container(容器)在UI框架中是一个很常见的概念,Flutter也不例外。'),
  ),
  //内边距
  padding: EdgeInsets.all(18.0),
  //外边距
  margin: EdgeInsets.all(44.0),
  width: 180.0,
  height: 240,
  //子Widget居中对齐
  /* alignment: Alignment.center,*/
  //Container样式
  decoration: BoxDecoration(
    //背景色
    color: Colors.red,
    //圆角边框
    borderRadius: BorderRadius.circular(10.0),
  ),
);
}

getPadding() {
//只需要设置边距 可以使用Padding
return Padding(
  padding: EdgeInsets.all(44.0),
  child: Text('我是Padding'),
);
}

getCenter() {
//直接居中
return Center(
  child: Text('center text'),
);
}

10.2 Row,Column,Expanded


//Row的用法示范
Row(
  children: <Widget>[
    Container(color: Colors.yellow, width: 60, height: 80,),
    Container(color: Colors.red, width: 100, height: 180,),
    Container(color: Colors.black, width: 60, height: 80,),
    Container(color: Colors.green, width: 60, height: 80,),
  ],
);

//Column的用法示范
Column(
  children: <Widget>[
    Container(color: Colors.yellow, width: 60, height: 80,),
    Container(color: Colors.red, width: 100, height: 180,),
    Container(color: Colors.black, width: 60, height: 80,),
    Container(color: Colors.green, width: 60, height: 80,),
  ],
);


//第一个和最后一个平分
Row(
  children: <Widget>[
    Expanded(flex: 1, child: Container(color: Colors.yellow, height: 60)), //设置了flex=1,因此宽度由Expanded来分配
    Container(color: Colors.red, width: 100, height: 180,),
    Container(color: Colors.black, width: 60, height: 80,),
    Expanded(flex: 1, child: Container(color: Colors.green,height: 60),)/设置了flex=1,因此宽度由Expanded来分配
  ],
);

对齐方式

控制大小

10.3 Stack,Positioned


Stack(
  children: <Widget>[
    Container(color: Colors.yellow, width: 300, height: 300),//黄色容器
    Positioned(
      left: 18.0,
      top: 18.0,
      child: Container(color: Colors.green, width: 50, height: 50),//叠加在黄色容器之上的绿色控件
    ),
    Positioned(
      left: 18.0,
      top:70.0,
      child: Text("Stack提供了层叠布局的容器"),//叠加在黄色容器之上的文本
    )
  ],
)

11. 自定义控件

11.1 组合控件

11.2 自定义控件

class WheelPainter extends CustomPainter {
  Paint getColoredPaint(Color color) {
    Paint paint = Paint();
    paint.color = color;
    return paint;
  }

  @override
  void paint(Canvas canvas, Size size) {
    //半径
    double wheelSize = min(size.width, size.height) / 2;
    //分成6份
    double nbElem = 6;
    //角度
    double radius = (2 * pi) / nbElem;
    //包裹饼图的矩形框  center:相对于原点的偏移量
    Rect boundingRect = Rect.fromCircle(
        center: Offset(wheelSize, wheelSize), radius: wheelSize);

    //每次画1/6圆
    canvas.drawArc(
        boundingRect, 0, radius, true, getColoredPaint(Colors.orange));
    canvas.drawArc(
        boundingRect, radius, radius, true, getColoredPaint(Colors.green));
    canvas.drawArc(
        boundingRect, radius * 2, radius, true, getColoredPaint(Colors.red));
    canvas.drawArc(
        boundingRect, radius * 3, radius, true, getColoredPaint(Colors.blue));
    canvas.drawArc(
        boundingRect, radius * 4, radius, true, getColoredPaint(Colors.pink));
    canvas.drawArc(boundingRect, radius * 5, radius, true,
        getColoredPaint(Colors.deepOrange));
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    //判断是否需要重绘,简单做下比较
    return oldDelegate != this;
  }
}

class Cake extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //CustomPaint是用来承载自定义View的容器,需要自定义一个画笔,得继承自CustomPainter
    return CustomPaint(
      size: Size(200, 200),
      painter: WheelPainter(),
    );
  }
}

12. 主题定制


MaterialApp(
  title: 'Flutter Demo',//标题
  theme: ThemeData(//设置主题
      brightness: Brightness.dark,//设置明暗模式为暗色
      accentColor: Colors.black,//(按钮)Widget前景色为黑色
      primaryColor: Colors.cyan,//主色调为青色
      iconTheme:IconThemeData(color: Colors.yellow),//设置icon主题色为黄色
      textTheme: TextTheme(body1: TextStyle(color: Colors.red))//设置文本颜色为红色
  ),
  home: MyHomePage(title: 'Flutter Demo Home Page'),
);
// 新建主题
Theme(
    data: ThemeData(iconTheme: IconThemeData(color: Colors.red)),
    child: Icon(Icons.favorite)
);
// 继承主题
Theme(
    data: Theme.of(context).copyWith(iconTheme: IconThemeData(color: Colors.green)),
    child: Icon(Icons.feedback)
);
Container(
    color: Theme.of(context).primaryColor,//容器背景色复用应用主题色
    child: Text(
      'Text with a background color',
      style: Theme.of(context).textTheme.title,//Text组件文本样式复用应用文本样式
    ));

13. 依赖管理

13.1 图片

flutter:
  assets:
    - assets/background.jpg   #挨个指定资源路径
    - assets/loading.gif  #挨个指定资源路径
    - assets/result.json  #挨个指定资源路径
    - assets/icons/    #子目录批量指定
    - assets/ #根目录也是可以批量指定的
目录如下

assets
├── background.jpg    //1.0x图
├── 2.0x
│   └── background.jpg  //2.0x图
└── 3.0x

在pubspec.yaml文件声明:

flutter:
  assets:
    - assets/background.jpg   #1.0x图资源

13.2 字体


fonts:
  - family: RobotoCondensed  #字体名字
    fonts:
      - asset: assets/fonts/RobotoCondensed-Regular.ttf #普通字体
      - asset: assets/fonts/RobotoCondensed-Italic.ttf 
        style: italic  #斜体
      - asset: assets/fonts/RobotoCondensed-Bold.ttf 
        weight: 700  #粗体

13.3 三方库 三方组件库

dependencies:
  //1. #路径依赖
  package1:
    path: ../package1/  
  //2. github
  date_format:
    git:
      url: https://github.com/xxx/package2.git #git依赖
  //3. pub上面的
  date_format: 1.0.6

14. 手势识别

Listener(
  child: Container(
    color: Colors.red,//背景色红色
    width: 300,
    height: 300,
  ),
  onPointerDown: (event) => print("down $event"),//手势按下回调
  onPointerMove:  (event) => print("move $event"),//手势移动回调
  onPointerUp:  (event) => print("up $event"),//手势抬起回调
);

//红色container坐标
double _top = 0.0;
double _left = 0.0;
Stack(//使用Stack组件去叠加视图,便于直接控制视图坐标
  children: <Widget>[
    Positioned(
      top: _top,
      left: _left,
      child: GestureDetector(//手势识别
        child: Container(color: Colors.red,width: 50,height: 50),//红色子视图
        onTap: ()=>print("Tap"),//点击回调
        onDoubleTap: ()=>print("Double Tap"),//双击回调
        onLongPress: ()=>print("Long Press"),//长按回调
        onPanUpdate: (e) {//拖动回调
          setState(() {
            //更新位置
            _left += e.delta.dx;
            _top += e.delta.dy;
          });
        },
      ),
    )
  ],
);

15. 跨组件共享数据

视图层级比较深的UI样式,直接通过属性传值会导致很多中间层增加冗余属性.

15.1 InheritedWidget

15.2 Notification

15.3 EventBus

15.4 对比

方式 数据流动方式 使用场景
属性传值 父到子 简单数据传递
InheritedWidget 父到子 跨层数据传递
Notification 子到父 状态通知
EventBus 发布订阅 消息批量同步

16. 路由管理

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      //打开页面
      onPressed: ()=> Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      // 回退页面
      onPressed: ()=> Navigator.pop(context)
    );
  }
}
MaterialApp(
    ...
    //注册路由
    routes:{
      "second_page":(context)=>SecondPage(),
    },
);
//使用名字打开页面
Navigator.pushNamed(context,"second_page");

MaterialApp(
    ...
    //注册路由
    routes:{
      "second_page":(context)=>SecondPage(),
    },
    //错误路由处理,统一返回UnknownPage
    onUnknownRoute: (RouteSettings setting) => MaterialPageRoute(builder: (context) => UnknownPage()),
);

//使用错误名字打开页面
Navigator.pushNamed(context,"unknown_page");

//打开页面时传递字符串参数
Navigator.of(context).pushNamed("second_page", arguments: "Hey");

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //取出路由参数
    String msg = ModalRoute.of(context).settings.arguments as String;
    return Text(msg);
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Text('Message from first screen: $msg'),
          RaisedButton(
            child: Text('back'),
            //页面关闭时传递参数
            onPressed: ()=> Navigator.pop(context,"Hi")
          )
        ]
      ));
  }
}

class _FirstPageState extends State<FirstPage> {
  String _msg='';
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Column(children: <Widget>[
        RaisedButton(
            child: Text('命名路由(参数&回调)'),
            //打开页面,并监听页面关闭时传递的参数
            onPressed: ()=> Navigator.pushNamed(context, "third_page",arguments: "Hey").then((msg)=>setState(()=>_msg=msg)),
        ),
        Text('Message from Second screen: $_msg'),

      ],),
    );
  }
}
上一篇 下一篇

猜你喜欢

热点阅读