Flutter基础

196

Flutter环境搭建

mac环境搭建

flutter doctor

206

创建项目

flutter create myapp
cd myapp
flutter devices // 查看开启的模拟器和连接的真机
flutter run -d xxxxxxxx
// 或
flutter run

运行

  1. 确保在VS Code的右下角选择了目标设备
  2. 按 F5 键或调用Debug>Start Debugging
  3. 等待应用程序启动
  4. 如果一切正常,在应用程序建成功后,您应该在您的设备或模拟器上看到应用程序:
    Starter App on Android

热重载

  • 开发工具启动,修改代码后cmd-s
  • 终端启动,输入r

Hello World

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcom to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}

MaterialApp

Material应用程序以MaterialApp widget开始, 该widget在应用程序的根部创建了一些有用的widget,其中包括一个Navigator, 它管理由字符串标识的Widget栈(即页面路由栈)。Navigator可以让您的应用程序在页面之间的平滑的过渡。 是否使用MaterialApp完全是可选的,但是使用它是一个很好的做法。

// 主要属性
home  主页
title 标题
color 颜色
theme 主题
routes 路由
。。。

Scaffold

Scaffold是一个路由页的骨架,appBar、drawer、bottomNavigationBar、floatingActionButton

Stateful widget & Stateless widget

  1. Stateful widget可以拥有状态,这些状态在widget生命周期中是可以变的,而Stateless widget是不可变的。
  2. Stateful widget至少由两个类组成:
    • 一个StatefulWidget类。
    • 一个 State类; StatefulWidget类本身是不变的,但是State类中持有的状态在widget生命周期中可能会发生变化。

基础组件

Text

属性名类型说明
textAlignTextAligncenter
right
left
justify
start
end
maxLines
overflowTextOverflowclip
ellipsis
fade
styleTextStyle()color
fontSize
height
fontFamily
background
decoration
decorationStyle
letterSpacing
textScaleFactor相对于当前字体大小的缩放因子1.5等
Text("hello",
  textAlign: TextAlign.right,
  style: TextStyle(
    color: Colors.blue,
    fontSize: 18.0,
    height: 1.2,
    background: new Paint()..color=Colors.yellow,
    decoration:TextDecoration.underline,
    decorationStyle: TextDecorationStyle.dashed
)

文字还有 TextSpan(Text.rich()) DefaultTextStyle

按钮

RaisedButton 漂浮 FlatButton 扁平 OutlineButton 带边框 IconButton 带Icon

RaisedButton(
  child: Text("normal"),
  onPressed: () {},
);

IconButton(
  icon: Icon(Icons.thumb_up),
  onPressed: () {},
)

带图标的按钮

RaisedButton.icon(
  icon: Icon(Icons.send),
  label: Text("发送"),
  onPressed: _onPressed,
),

相关属性

const FlatButton({
  ...  
  @required this.onPressed, //按钮点击回调
  this.textColor, //按钮文字颜色
  this.disabledTextColor, //按钮禁用时的文字颜色
  this.color, //按钮背景颜色
  this.disabledColor,//按钮禁用时的背景颜色
  this.highlightColor, //按钮按下时的背景颜色
  this.splashColor, //点击时,水波动画中水波的颜色
  this.colorBrightness,//按钮主题,默认是浅色主题 
  this.padding, //按钮的填充
  this.shape, //外形
  @required this.child, //按钮的内容
})

图片

本地图片

  1. pubspec.yaml中的flutter部分添加如下内容:

      assets:
        - images/avatar.png
    
  2. 加载该图片

    Image(
      image: AssetImage("images/avatar.png"),
      width: 100.0
    );
    // 或
    Image.asset("images/avatar.png",
      width: 100.0,
    )
    

网络图片

Image(
  image: NetworkImage(
      "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),
  width: 100.0,
)
// 或
Image.network(
  "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
  width: 100.0,
)

fit(BoxFit.fill)

  • fill:会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。
  • cover:会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。
  • contain:这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。
  • fitWidth:图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
  • fitHeight:图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
  • none:图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。

单选/多选框

Switch(
  value: _switchSelected,//当前状态
  onChanged:(value){
  //重新构建页面  
    setState(() {
      _switchSelected=value;
    });
  },
),
Checkbox(
  value: _checkboxSelected,
  activeColor: Colors.red, //选中时的颜色
  onChanged:(value){
    setState(() {
      _checkboxSelected=value;
    });
  },
)

输入框/表单

const TextField({
  ...
  TextEditingController controller, 
  FocusNode focusNode,
  InputDecoration decoration = const InputDecoration(),
  TextInputType keyboardType,
  TextInputAction textInputAction,
  TextStyle style,
  TextAlign textAlign = TextAlign.start,
  bool autofocus = false,
  bool obscureText = false,
  int maxLines = 1,
  int maxLength,
  bool maxLengthEnforced = true,
  ValueChanged<String> onChanged,
  VoidCallback onEditingComplete,
  ValueChanged<String> onSubmitted,
  List<TextInputFormatter> inputFormatters,
  bool enabled,
  this.cursorWidth = 2.0,
  this.cursorRadius,
  this.cursorColor,
  ...
})

获取输入内容

//定义一个controller
TextEditingController _unameController = TextEditingController();

TextField(
    autofocus: true,
    controller: _unameController, //设置controller
    ...
)

print(_unameController.text)

监听值的变化

  1. 设置onChange回调,如:

    TextField(
        autofocus: true,
        onChanged: (v) {
          print("onChange: $v");
        }
    )
    
  2. 通过controller监听,如:

    @override
    void initState() {
      //监听输入改变  
      _unameController.addListener((){
        print(_unameController.text);
      });
    }
    

进度条

LinearProgressIndicator CircularProgressIndicator

容器组件

Container

Container({
  this.alignment,
  this.padding, //容器内补白,属于decoration的装饰范围
  Color color, // 背景色
  Decoration decoration, // 背景装饰
  Decoration foregroundDecoration, //前景装饰
  double width,//容器的宽度
  double height, //容器的高度
  BoxConstraints constraints, //容器大小的限制条件
  this.margin,//容器外补白,不属于decoration的装饰范围
  this.transform, //变换
  this.child,
})

AppBar

AppBar({
  Key key,
  this.leading, //导航栏最左侧Widget,常见为抽屉菜单按钮或返回按钮。
  this.automaticallyImplyLeading = true, //如果leading为null,是否自动实现默认的leading按钮
  this.title,// 页面标题
  this.actions, // 导航栏右侧菜单
  this.bottom, // 导航栏底部菜单,通常为Tab按钮组
  this.elevation = 4.0, // 导航栏阴影
  this.centerTitle, //标题是否居中 
  this.backgroundColor,
  ...   //其它属性见源码注释
})

TabBar

class _ScaffoldRouteState extends State<ScaffoldRoute>
    with SingleTickerProviderStateMixin {

  TabController _tabController; //需要定义一个Controller
  List tabs = ["新闻", "历史", "图片"];

  @override
  void initState() {
    super.initState();
    // 创建Controller  
    _tabController = TabController(length: tabs.length, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        ... //省略无关代码
        bottom: TabBar(   //生成Tab菜单
          controller: _tabController,
          tabs: tabs.map((e) => Tab(text: e)).toList()
        ),
        body: TabBarView(
        controller: _tabController,
        children: tabs.map((e) { //创建3个Tab页
          return Container(
            alignment: Alignment.center,
            child: Text(e, textScaleFactor: 5),
          );
        }).toList(),
      ),
      ... //省略无关代码

  }

抽屉菜单Drawer FloatingActionButton bottomNavigationBar略

Padding

Padding({
  ...
  EdgeInsetsGeometry padding,
  Widget child,
})
  • fromLTRB(double left, double top, double right, double bottom):分别指定四个方向的填充。
  • all(double value) : 所有方向均使用相同数值的填充。
  • only({left, top, right ,bottom }):可以设置具体某个方向的填充(可以同时指定多个方向)。
  • symmetric({ vertical, horizontal }):用于设置对称方向的填充,verticaltopbottomhorizontalleftright
Padding(
  //左边添加8像素补白
  padding: const EdgeInsets.all(8.0),
  child: Text("Hello world"),
)
  
const EdgeInsets.fromLTRB(1.0, 2.0, 3.0, 4.0)
const EdgeInsets.only({left: 2.0})
const EdgeInsets.symmetric({1.0, 2.0})

尺寸限制类容器(ConstrainedBox/SizedBox…)

ConstrainedBox

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity, //宽度尽可能大
    minHeight: 50.0 //最小高度为50像素
  ),
  child: Container(
      height: 5.0, 
      child: redBox 
  ),
)

BoxConstraints用于设置限制条件,它的定义如下:

const BoxConstraints({
  this.minWidth = 0.0, //最小宽度
  this.maxWidth = double.infinity, //最大宽度
  this.minHeight = 0.0, //最小高度
  this.maxHeight = double.infinity //最大高度
})

SizedBox

SizedBox用于给子元素指定固定的宽高,如:

SizedBox(
  width: 80.0,
  height: 80.0,
  child: redBox
)

UnconstrainedBox 不会对子组件产生任何限制

AspectRatio 指定子组件的长宽比

LimitedBox 指定最大宽高

FractionallySizedBox 根据父容器宽高的百分比来设置子组件宽高

装饰容器(DecoratedBox)

const DecoratedBox({
  Decoration decoration,
  DecorationPosition position = DecorationPosition.background,
  Widget child
})

BoxDecoration

BoxDecoration({
  Color color, //颜色
  DecorationImage image,//图片
  BoxBorder border, //边框
  BorderRadiusGeometry borderRadius, //圆角
  List<BoxShadow> boxShadow, //阴影,可以指定多个
  Gradient gradient, //渐变
  BlendMode backgroundBlendMode, //背景混合模式
  BoxShape shape = BoxShape.rectangle, //形状
})
 DecoratedBox(
    decoration: BoxDecoration(
      gradient: LinearGradient(colors:[Colors.red,Colors.orange[700]]), //背景渐变
      border: Border.all(width: 2.0, color:Colors.red),
      borderRadius: BorderRadius.circular(3.0), //3像素圆角
      boxShadow: [ //阴影
        BoxShadow(
            color:Colors.black54,
            offset: Offset(2.0,2.0),
            blurRadius: 4.0
        )
      ]
    ),
  child: Padding(padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),
    child: Text("Login", style: TextStyle(color: Colors.white),),
  )
)

剪切(Clip)

剪裁Widget作用
ClipOval子组件为正方形时剪裁为内贴圆形,为矩形时,剪裁为内贴椭圆
ClipRRect将子组件剪裁为圆角矩形
ClipRect剪裁子组件到实际占用的矩形大小(溢出部分剪裁)
ClipOval(child: avatar), //剪裁为圆形
ClipRRect( //剪裁为圆角矩形
  borderRadius: BorderRadius.circular(5.0),
  child: avatar,
),

Transform 变换

布局组件

线性布局Row/Column

Row({
  ...  
  TextDirection textDirection,    
  MainAxisSize mainAxisSize = MainAxisSize.max,    
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  VerticalDirection verticalDirection = VerticalDirection.down,  
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  List<Widget> children = const <Widget>[],
})
  • textDirection:表示水平方向子组件的布局顺序(是从左往右还是从右往左),默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左)。
  • mainAxisSize:表示Row在主轴(水平)方向占用的空间,默认是MainAxisSize.max,表示尽可能多的占用水平方向的空间,此时无论子widgets实际占用多少水平空间,Row的宽度始终等于水平方向的最大宽度;而MainAxisSize.min表示尽可能少的占用水平空间,当子组件没有占满水平剩余空间,则Row的实际宽度等于所有子组件占用的的水平空间;
  • mainAxisAlignment:表示子组件在Row所占用的水平空间内对齐方式,如果mainAxisSize值为MainAxisSize.min,则此属性无意义,因为子组件的宽度等于Row的宽度。只有当mainAxisSize的值为MainAxisSize.max时,此属性才有意义,MainAxisAlignment.start表示沿textDirection的初始方向对齐,如textDirection取值为TextDirection.ltr时,则MainAxisAlignment.start表示左对齐,textDirection取值为TextDirection.rtl时表示从右对齐。而MainAxisAlignment.endMainAxisAlignment.start正好相反;MainAxisAlignment.center表示居中对齐。读者可以这么理解:textDirectionmainAxisAlignment的参考系。
  • verticalDirection:表示Row纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
  • crossAxisAlignment:表示子组件在纵轴方向的对齐方式,Row的高度等于子组件中最高的子元素高度,它的取值和MainAxisAlignment一样(包含startendcenter三个值),不同的是crossAxisAlignment的参考系是verticalDirection,即verticalDirection值为VerticalDirection.downcrossAxisAlignment.start指顶部对齐,verticalDirection值为VerticalDirection.up时,crossAxisAlignment.start指底部对齐;而crossAxisAlignment.endcrossAxisAlignment.start正好相反;
  • children :子组件数组

Column与Row参数相同

⚠️如果Row里面嵌套Row,或者Column里面再嵌套Column,那么只有最外面的RowColumn会占用尽可能大的空间,里面RowColumn所占用的空间为实际大小。

如果要让里面的Column占满外部Column,可以使用Expanded 组件

Flex

Flex({
  ...
  @required this.direction, //弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
  List<Widget> children = const <Widget>[],
})

Expanded可实现比例分配

//Flex的两个子widget按1:2来占据水平空间  
Flex(
  direction: Axis.horizontal,
  children: <Widget>[
    Expanded(
      flex: 1,
      child: Container(
        height: 30.0,
        color: Colors.red,
      ),
    ),
    Expanded(
      flex: 2,
      child: Container(
        height: 30.0,
        color: Colors.green,
      ),
    ),
  ],
),

流式布局

Wrap({
  ...
  this.direction = Axis.horizontal,
  this.alignment = WrapAlignment.start,
  this.spacing = 0.0,  // 主轴方向子widget的间距
  this.runAlignment = WrapAlignment.start,  //  纵轴方向的对齐方式
  this.runSpacing = 0.0,  // 纵轴方向的间距
  this.crossAxisAlignment = WrapCrossAlignment.start,
  this.textDirection,
  this.verticalDirection = VerticalDirection.down,
  List<Widget> children = const <Widget>[],
})

207

Flow组件 略

层叠布局(Stack/Position)

Stack允许子组件堆叠,而Positioned用于根据Stack的四个角来确定子组件的位置

Stack

Stack({
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.overflow = Overflow.clip,
  List<Widget> children = const <Widget>[],
})

positioned

const Positioned({
  Key key,
  this.left, 
  this.top,
  this.right,
  this.bottom,
  this.width,
  this.height,
  @required Widget child,
})
//通过ConstrainedBox来确保Stack占满屏幕
ConstrainedBox(
  constraints: BoxConstraints.expand(),
  child: Stack(
    alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
    children: <Widget>[
      Container(child: Text("Hello world",style: TextStyle(color: Colors.white)),
        color: Colors.red,
      ),
      Positioned(
        left: 18.0,
        child: Text("I am Jack"),
      ),
      Positioned(
        top: 18.0,
        child: Text("Your friend"),
      )        
    ],
  ),
);

208

对齐与相对定位(Align)

Align({
  Key key,
  this.alignment = Alignment.center,
  this.widthFactor,
  this.heightFactor,
  Widget child,
})

Center 组件继承Align, alignment值为Alignment.center

滚动组件

ListView

ListView({
  ...  
  //可滚动widget公共参数
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController controller,
  bool primary,
  ScrollPhysics physics,
  EdgeInsetsGeometry padding,

  //ListView各个构造函数的共同参数  
  double itemExtent,
  bool shrinkWrap = false,
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  double cacheExtent,

  //子widget列表
  List<Widget> children = const <Widget>[],
})

ListView.builder

child: ListView.builder(
  itemCount: data.length,
  itemBuilder: (context, index) {
    _buildListItem(data.[index]);
  },
),

GridView

GridView({
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController controller,
  bool primary,
  ScrollPhysics physics,
  bool shrinkWrap = false,
  EdgeInsetsGeometry padding,
  @required SliverGridDelegate gridDelegate, //控制子widget layout的委托
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  double cacheExtent,
  List<Widget> children = const <Widget>[],
}

SliverGridDelegateWithFixedCrossAxisCount

SliverGridDelegateWithFixedCrossAxisCount({
  @required double crossAxisCount, 
  double mainAxisSpacing = 0.0,
  double crossAxisSpacing = 0.0,
  double childAspectRatio = 1.0,
})
GridView.count( 
  crossAxisCount: 3,
  childAspectRatio: 1.0,
  children: <Widget>[
    Icon(Icons.ac_unit),
    Icon(Icons.airport_shuttle),
    Icon(Icons.all_inclusive),
    Icon(Icons.beach_access),
    Icon(Icons.cake),
    Icon(Icons.free_breakfast),
  ],
);

SliverGridDelegateWithMaxCrossAxisExtent

SliverGridDelegateWithMaxCrossAxisExtent({
  double maxCrossAxisExtent,  // 子元素在横轴上的最大长度
  double mainAxisSpacing = 0.0,
  double crossAxisSpacing = 0.0,
  double childAspectRatio = 1.0,
})
GridView.extent(
   maxCrossAxisExtent: 120.0,
   childAspectRatio: 2.0,
   children: <Widget>[
     Icon(Icons.ac_unit),
     Icon(Icons.airport_shuttle),
     Icon(Icons.all_inclusive),
     Icon(Icons.beach_access),
     Icon(Icons.cake),
     Icon(Icons.free_breakfast),
   ],
 );

关注公众号: 页面仔小杨 【实战干货、原创分享】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值