Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flutter入门以及常用Widget介绍 #19

Open
Nealyang opened this issue Mar 19, 2019 · 2 comments
Open

flutter入门以及常用Widget介绍 #19

Nealyang opened this issue Mar 19, 2019 · 2 comments
Labels

Comments

@Nealyang
Copy link
Owner

前言

关于Flutter入门的文章非常多,常用Widget总结文章也非常多,之前写过相关入门文章,欢迎大家查看:flutter从入门到能寄几玩儿,这里我们还是以上面文章为主,先把项目跑起来,然后走马观花常用布局、常用Widget。

相关文章推荐

Flutter 中文网

Flutter 环境搭建

Flutter widget

Flutter走马观花

关于Flutter环境问题可以查阅上方链接,步骤都非常详细。
此后~大量代码来袭

基础Widget之material版Hello world

国际惯例,hello world

import 'package:flutter/material.dart';

class MyAppBar extends StatelessWidget{
  MyAppBar({this.title});//
  final Widget title;

  @override
  Widget build(BuildContext context){
    return new Container(
      height: 56.0,
      padding: const EdgeInsets.symmetric(horizontal:8.0),
      decoration: new BoxDecoration(
        color:Colors.blue[400]
      ),
      child: Row(
        children: <Widget>[
          new IconButton(
            icon:new Icon(Icons.menu),
            tooltip:'Navigation menu',
            onPressed: (){
              print('点击Menu');
            },
          ),
          new Expanded(
            child:new Center(
              child:title
            )
          ),
          new IconButton(
            icon:Icon(Icons.search),
            tooltip:'Search',
            onPressed: (){
              print('点击搜索按钮');
            },
          )
        ],
      ),
    );
  }
}

class MyScaffold extends StatelessWidget{
  @override 
  Widget build(BuildContext context){
    return Material(
      child: new Column(
        children:<Widget>[
          new MyAppBar(
            title:new Text(
              'Hello World',
              style:Theme.of(context).primaryTextTheme.title
             ),
          ),
          new Expanded(
            child:new Center(
              child:Text('Hello World!!!')
            )
          )
        ]
      ),
    );
  }
}

void main(){
  runApp(
    new MaterialApp(
      title:'My app',
      home:new MyScaffold()
    )
  );
}

img
代码地址:https://github.com/Nealyang/flutter

这个UI的确有些对不起人了,上面的title被挡住了。且先不去适配,后面我们使用Material提供的Scaffold即可

第一个例子,重点说下代码(用过的Widget记住):

  • 一切都是Widget,且Widget前面的new可有可无。
  • 类MyAppBar和MyScaffold中使用了Container、Row、Column、Text、IconButton、Icon、BoxDecoration、Center、Expanded等常用Widget
    • Container一个拥有绘制、定位、调整大小的 widget。类似于div,我们可以用它来创建矩形视图,container 可以装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container可以使用矩阵在三维空间中对其进行变换。
    • RowColumn其实就是flex布局中的flex-direction
    • Expanded它会填充尚未被其他子项占用的的剩余可用空间。Expanded可以拥有多个children。然后使用flex参数来确定他们占用剩余空间的比例。更多细节可以参看:flutter控件Flexible和 Expanded的区别
  • 先定义了一个MyAppBar的类,构造函数中接受一个Widget的title,其实我们也可以接受String title然后在类中自己去new Title(title)
  • runApp函数接受给定的Widget并使用其作为widget根。
  • widget的主要工作是实现一个build函数,用以构建自身。一个widget通常由一些较低级别widget组成。Flutter框架将依次构建这些widget,直到构建到最底层的子widget时,这些最低层的widget通常为RenderObject,它会计算并描述widget的几何形状。

基本交互之material版Hello world

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // app的根Widget
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        // 这是设置的app主题
        // 运行后你可以看到app有一个蓝色的toobar,并且在不退出app的情况下修改代码会热更新
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

// 这是应用中一个基类,继承自StateFulWidget,意味着这个类拥有一个state对象,该对象里的一些字段会影响app的UI
// 这个类是state的一些配置项。通过构造函数来获取值,这个值一般在State中消费,并且使用final关键字。其实类似于react中的defaultProps

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // setState方法告诉Flutter,这个State中有些值发生了变化,以便及时将新值更新到UI上,
      // 如果我不通过setState更改_count字段,那么Flutter并不会调用build匿名函数去更新界面
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // build方法会在每次setState的时候重新运行,例如上面的_incrementCounter方法被调用
    //Flutter已经被优化了重新构建的方法,所以你只会去更新需要去更新的部分,不必去单独更新里面的一些更细小的widget,类似于React中diff
    return new Scaffold(
      appBar: new AppBar(
        // 这里我们使用从App.build方法中初始化MyHomePage时候传入的title值来设置我们的title
        title: new Text(widget.title),
      ),
      body: new Center(
        // Center是一个布局Widget,他只有一个child(区分row or cloumn等是children),并且会将child的widget居中显示
        child: new Column(
          // Column也是一个布局widget,他可以有多个子widget
          // Column 有很多的属性去控制他的大小以及子widget的位置,这里我们使用mainAxisAlignment来让children在垂直线上居中,
          // 这里的主轴就是垂直的,因为Column就是垂直方向的,这里可以大概想象为display:flex,flex-directions:column,align-item,justifyContent。。。
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'Hello World!',
              style:TextStyle(
                fontSize:24.0,
                color: Colors.redAccent,
                decorationStyle:TextDecorationStyle.dotted,
                fontWeight: FontWeight.bold,
                fontStyle: FontStyle.italic,
                decoration: TextDecoration.underline
              )
            ),
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),//最后这个逗号有利于格式化代码
    );
  }
}

img
注释上基本已经加了,这里重点说下,StatefulWidget和StatelessWidget.

  • Stateless widgets 是不可变的,这意味着它们的属性不能改变——所有的值都是 final
  • Stateful widgets 持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少需要两个类:1)一个 StatefulWidget 类;2)一个 State 类,StatefulWidget 类本身是不变的,但是 State 类在 widget 生命周期中始终存在
  • 如果需要变化需要重新创建。StatefulWidget可以保存自己的状态。那问题是既然widget都是immutable的,怎么保存状态?其实Flutter是通过引入了State来保存状态。当State的状态改变时,能重新构建本节点以及孩子的Widget树来进行UI变化。注意:如果需要主动改变State的状态,需要通过setState()方法进行触发,单纯改变数据是不会引发UI改变的。

还有关于key的部分这里就不做介绍了,其实就类似与react中key的概念,便于diff,提高效率的。
具体可以查看 Key

到这里,我们看到了Flutter的一些基本用法,Widget的套用、样式的编写、事件的注册,如果再学习下一些路由、请求、缓存是不是就可以自己开发APP了呢!

OK,强化下编写界面,咱再来些demo吧~

布局Widget

img

自己写的后,发现跟官网实现方式不同,代码地址

具体实现可以参照官网教程

这里不再赘述,下面我们说下对于布局的理解和感受以及常用布局widget。

从一个前端的角度来说,说到画界面,可能还是对布局这块比较敏感

img

img

当然,这里我们还是说下目前常用的flex布局,基本拿到页面从大到小拆分后就是如上图。

所以Widget布局其实也就是Row和Column用的最多,然后由于Flutter一切皆为组件的理念,可能会需要用到别的类css布局的Widget,譬如:Container。其实咱就理解为块元素吧!

下面简单演示下一些常用的Widget,这里就不在赘述Row和Column了。传送门:布局Widget

Container

可以添加padding、margin、border、background color、通常用于装饰其他Widget

img

代码链接 Nealyang/flutter

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    Container cell (String imgSrc){
      return new Container(
        decoration: new BoxDecoration(
          border:Border.all(width:6.0,color:Colors.black38),
          borderRadius: BorderRadius.all(const Radius.circular(8.0))
        ),
        child: Image.asset(
          'images/$imgSrc',
          width: 180.0,
          height: 180.0,
          fit: BoxFit.cover,
        ),
      );
    }

    return Container(
      padding: const EdgeInsets.all(10.0),
      color: Colors.grey,
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        children:<Widget>[
          new Container(
            margin: const EdgeInsets.only(bottom:10.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children:<Widget>[
                cell('1.jpg'),
                cell('2.jpg')
              ]
            ),
          ),
          new Container(
            margin: const EdgeInsets.only(bottom:10.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children:<Widget>[
                cell('3.jpg'),
                cell('4.jpg')
              ]
            ),
          ),
        ]
      ),
    );
  }
}

该布局中每个图像使用一个Container来添加一个圆形的灰色边框和边距。然后使用容器将列背景颜色更改为浅灰色。

GridView

可滚动的网格布局,理解为display:grid

GridView提供两个预制list,当GridView检测到内容太长时,会自动滚动。如果需要构建自定义grid,可是使用GridView.countGridView.extent来指定允许设置的列数以及指定项最大像素宽度。

img

代码链接 Nealyang/flutter

List<Container> _buildGridTileList(int count) {

  return new List<Container>.generate(
      count,
      (int index) =>
          new Container(child: new Image.asset('images/${index+1}.jpg')));
}

Widget buildGrid() {
  return new GridView.extent(
      maxCrossAxisExtent: 150.0,
      padding: const EdgeInsets.all(4.0),
      mainAxisSpacing: 4.0,
      crossAxisSpacing: 4.0,
      children: _buildGridTileList(10));
}

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return  new Center(
        child: buildGrid(),
      );
  }
}

如上是指定maxCrossAxisExtent,我们可以直接去指定列数,例如官网的代码实例:

new GridView.count(
  primary: false,
  padding: const EdgeInsets.all(20.0),
  crossAxisSpacing: 10.0,
  crossAxisCount: 3,
  children: <Widget>[
    const Text('He\'d have you all unravel at the'),
    const Text('Heed not the rabble'),
    const Text('Sound of screams but the'),
    const Text('Who scream'),
    const Text('Revolution is coming...'),
    const Text('Revolution, they...'),
  ],
)

通过crossAxisCount直接指定列数。

Stack

层叠布局,position为absolute的感jio~

使用Stack来组织需要重叠的widget。widget可以完全或部分重叠底部widget。子列表中的第一个widget是base widget; 随后的子widget被覆盖在基础widget的顶部。Stack的内容不能滚动。有点类似于weex中的设置了absolute的感觉。底部组件永远在上面组件的上面。

ListView

可滚动的长列表,可以水平或者垂直。

Card

Material风格组件,卡片,AntD啥的组件库经常会出现的那种组件。

在flutter中,Card具有圆角和阴影,更改Card的elevation属性可以控制阴影效果。

ListTile

Material风格组件,我理解为常用的列表Item的样式,最多三行文字,可选的行前、行尾的图标

img

代码链接 Nealyang/flutter

总结

撇开框架设计原理和思想不谈,其实技术就是一个工具,所谓实践出真知,我们需要的更多是掌握查阅这些widget的技巧以及常用widget在项目实践中的熟练掌握。

对于画界面,更多的还可以参看下官网教程:Flutter for Web开发者

下面让我们正式开始我们的《Flutter入门实战:从0到1仿写web版掘金App》吧!!!

@wg19pmme
Copy link

图片全部够挂了,全局代理都打不开

@lanbery
Copy link

lanbery commented Mar 4, 2024

图片全部够挂了,全局代理都打不开

图片服务到期了吧,不如直接用GitHub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants