Flutter入門04----基礎(chǔ)Widget

項(xiàng)目案例 -- StatelessWidget

  • 案例代碼如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: SFContentBody(),
    );
  }
}

class SFContentBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
     return ListView(
       children: [
         SFProductItem("Apple1","MacBook1","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
         SFProductItem("Apple2","MacBook2","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
         SFProductItem("Apple3","MacBook3","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
       ],
     );
  }
}

class SFProductItem extends StatelessWidget {
  final String title;
  final String desc;
  final String imageUrl;

  final titleStyle = TextStyle(fontSize: 25,color: Colors.orange);
  final descStyle = TextStyle(fontSize: 20,color: Colors.green);

  //自定義構(gòu)造函數(shù)
  SFProductItem(this.title,this.desc,this.imageUrl);
  
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(8), //設(shè)置內(nèi)邊距
      decoration: BoxDecoration(
        border: Border.all(
          width: 5, //設(shè)置邊框的寬度
          color: Colors.purple //設(shè)置邊框的額顏色
        )
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(title,style: titleStyle),
          SizedBox(height: 8),//設(shè)置間距
          Text(desc,style: descStyle),
          SizedBox(height: 8),
          Image.network(imageUrl)
        ],
      ),
    );
  }
}
  • 效果如下所示:
image.png
  • 給widget添加Container的快捷鍵Alt + Enter
  • widget之間設(shè)置間距使用SizedBox(height: 8)

項(xiàng)目案例 -- StatefulWidget

  • StatefulWidget最大的特點(diǎn)是:StatefulWidget通過創(chuàng)建狀態(tài)類_SFHomeContentState墙基,來管理自己的狀態(tài)數(shù)據(jù)棒妨;
  • 案例代碼如下:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

void main() => runApp(SFMyApp());


class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表")
      ),
      body: SFHomeContent("上面是一個(gè)簡單的計(jì)數(shù)器")
    );
  }
}

class SFHomeContent extends StatefulWidget {
  final String message;

  SFHomeContent(this.message);

  @override
  State<StatefulWidget> createState() {
    return _SFHomeContentState();
  }
}

//
class _SFHomeContentState extends State<SFHomeContent>{

  var _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          _getButtons(),
          Text("當(dāng)前計(jì)數(shù): $_counter",style: TextStyle(fontSize: 20)),
          Text("${widget.message}",style: TextStyle(fontSize: 18))
        ],
      ),
    );
  }

  Widget _getButtons(){
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        RaisedButton(
          child: Text("+",style: TextStyle(fontSize: 20,color: Colors.white)),
          color: Colors.pink,
          onPressed: (){
            print("點(diǎn)擊+");
            setState(() {
              _counter++;
            });
          },
        ),
        RaisedButton(
          child: Text("-",style: TextStyle(fontSize: 20,color: Colors.white)),
          color: Colors.purple,
          onPressed: (){
            print("點(diǎn)擊-");
            setState(() {
              _counter--;
            });
          },
        )
      ],
    );
  }
}
  • class _SFHomeContentState extends State<SFHomeContent>蠢络,Widget _getButtons()類名與方法名之前加下劃線表明屬于私有的限煞;
  • class _SFHomeContentState extends State<SFHomeContent>_SFHomeContentState狀態(tài)用來管理SFHomeContent這個(gè)widget的狀態(tài)數(shù)據(jù)的,即_SFHomeContentState會(huì)綁定SFHomeContent融涣,_SFHomeContentState中能通過widget屬性訪問SFHomeContent中的內(nèi)容稀颁;
  • 代碼運(yùn)行效果:
image.png

StatefulWidget生命周期

  • 所謂生命周期是指:目標(biāo)組件從創(chuàng)建到銷毀的整個(gè)過程肢藐,監(jiān)聽組件的生命周期以便在不同的時(shí)期執(zhí)行不同的邏輯吹菱;
  • Flutter組件的生命周期:
    • StatelessWidget可以由父widget直接傳入值,調(diào)用build方法來創(chuàng)建彭则,整個(gè)過程非常簡單鳍刷,其生命周期,主要關(guān)注構(gòu)造函數(shù)build方法俯抖;
    • StatefulWidget需要通過State來管理其狀態(tài)數(shù)據(jù)输瓜,并且監(jiān)聽狀態(tài)的改變重新build整個(gè)widget;
  • StatefulWidget生命周期的過程如下圖所示:
    Snip20211018_14.png
  • 1.執(zhí)行StatefulWidget的構(gòu)造函數(shù)芬萍;
  • 2.執(zhí)行StatefulWidgetcreateState方法尤揣,創(chuàng)建一個(gè)維護(hù)StatefulWidgetState對(duì)象;
  • 3.執(zhí)行State的構(gòu)造方法柬祠;
  • 4.執(zhí)行initState方法北戏,我們通常在此方法中執(zhí)行一些數(shù)據(jù)初始化操作,或者發(fā)送網(wǎng)絡(luò)請(qǐng)求漫蛔;
  • 5.didChangeDependencies方法嗜愈,在下面兩種情況下會(huì)調(diào)用:
    • 調(diào)用initState時(shí)會(huì)調(diào)用旧蛾;
    • 從其他對(duì)象中依賴一些數(shù)據(jù)發(fā)生改變時(shí),比如InheritedWidget蠕嫁;
  • 6.執(zhí)行build方法锨天,渲染widget樹;
  • 7.當(dāng) 當(dāng)前的widget不再使用時(shí)剃毒,會(huì)調(diào)用dispose方法進(jìn)行銷毀病袄;
  • 8.手動(dòng)調(diào)用setState方法,會(huì)根據(jù)最新的狀態(tài)數(shù)據(jù)來重新調(diào)用build方法赘阀,構(gòu)建對(duì)應(yīng)的widget益缠;
  • 9.執(zhí)行didUpdateWidget方法是在父widget觸發(fā)重建rebuild時(shí),系統(tǒng)會(huì)調(diào)用didUpdateWidget方法纤壁;
  • 代碼案例驗(yàn)證:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());


class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表")
      ),
      body: SFHomeContent("上面是一個(gè)簡單的計(jì)數(shù)器")
    );
  }
}

class SFHomeContent extends StatefulWidget {
  final String message;

  SFHomeContent(this.message){
    print("SFHomeContent 構(gòu)造方法");
  }

  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent>{

  var _counter = 0;

  _SFHomeContentState(){
    print("_SFHomeContentState 構(gòu)造方法");
  }

  @override
  void initState() {
    super.initState();
    print("_SFHomeContentState initState");
  }

  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          RaisedButton(
            child: Text("+",style: TextStyle(fontSize: 25,color: Colors.white)),
            color: Colors.pinkAccent,
            onPressed: (){
              setState(() {
                _counter++;
              });
            },
          ),
          Text("${widget.message}",style: TextStyle(fontSize: 18))
        ],
      ),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("_SFHomeContentState didChangeDependencies");
  }

  @override
  void didUpdateWidget(SFHomeContent oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("_SFHomeContentState didUpdateWidget");
  }

- 

  @override
  void dispose() {
    super.dispose();
    print("_SFHomeContentState dispose");
  }
}
  • 執(zhí)行結(jié)果如下:
image.png
  • StatefulWidget生命周期復(fù)雜版的過程如下圖所示:

    Snip20211018_13.png

  • mounded時(shí)State內(nèi)部設(shè)置的一個(gè)屬性左刽,不需要我們手動(dòng)進(jìn)行修改的,其主要作用是記錄widget對(duì)應(yīng)的element是否為空酌媒;

  • dirty state含義是臟的state欠痴,它實(shí)際是來標(biāo)記Element的,標(biāo)記為dirty的Element會(huì)等待下一次的重繪檢查秒咨,強(qiáng)制調(diào)用build方法來構(gòu)建我們的widget喇辽;

  • clean state含義是干凈的state,它表示當(dāng)前build出來的widget雨席,下一次重繪檢查不需要重新build菩咨;

Flutter的編程范式

  • 命令式編程:就是一步步給計(jì)算機(jī)命令,告訴它我們想做什么事情陡厘;
  • 聲明式編程:通常是描述目標(biāo)的性質(zhì)抽米,依賴哪些狀態(tài),并且當(dāng)依賴的狀態(tài)發(fā)生改變時(shí)糙置,我們通過某些方式通知目標(biāo)做出響應(yīng)云茸,聲明式編程是依賴框架的;
  • Flutter采用的是聲明式編程谤饭;
  • 命令式編程的代碼案例:
final text = new Text();
var title = "Hello World";
text.setContent(title); //主動(dòng)設(shè)置title
  • 聲明式編程的代碼案例:
var title = "Hello World";
Text(title); //告訴Text內(nèi)部顯示的是title

基礎(chǔ)Widget

文本W(wǎng)idget
  • 在Flutter中使用Text組件來顯示文本标捺;
普通文本
  • 控制文本布局的參數(shù):在構(gòu)造函數(shù)中;
    • textAlign:文本的對(duì)齊方式揉抵;
    • textDirection:文本的排版方向亡容;
    • maxLines:最大顯示行數(shù);
    • overflow:文本的截?cái)嘁?guī)則冤今;
  • 控制文本樣式的參數(shù):在構(gòu)造函數(shù)的參數(shù)style中闺兢;
    • fontSize:文本大小戏罢;
    • color:文本顏色列敲;
    • fontFamily:設(shè)置字體阱佛;
    • shaow:文本陰影;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return TextDemo();
  }
}

class TextDemo extends StatelessWidget {
  const TextDemo({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      "基礎(chǔ)widget \ndasd has發(fā)哈就困了打算看是的撒 \n但是開發(fā)雙卡雙待",
      textAlign: TextAlign.center,
      maxLines: 2,
      overflow: TextOverflow.ellipsis,
      style: TextStyle(
        fontSize: 30,
        color: Colors.red,
        fontWeight: FontWeight.bold,
        fontFamily: 'Courier'
      ),
    );
  }
}
富文本
  • 富文本使用Text.rich戴而,其中必選參數(shù)傳入InlineSpan凑术,是一個(gè)抽象類,我們需要傳入其子類即可所意,其子類有:
    • TextSpan:顯示文本的淮逊;
    • WidgetSpan:顯示圖片的;
    • placeholderSpace
  • 案例代碼如下:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return TextRichDemo();
  }
}

class TextRichDemo extends StatelessWidget {
  const TextRichDemo({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text.rich(
      TextSpan(
        children: [
          TextSpan(text: "Hello World!!", style: TextStyle(fontSize: 20,color: Colors.green)),
          TextSpan(text: "Hello iOS!!", style: TextStyle(fontSize: 20,color: Colors.red)),
          WidgetSpan(child: Icon(Icons.favorite,color: Colors.red)),
          TextSpan(text: "Hello Flutter!!", style: TextStyle(fontSize: 25,color: Colors.orange))
        ]
      )
    );
  }
}
按鈕Widget
  • 常見的按鈕類型有:
    • RaisedButton
    • FlatButton
    • OutlineButton
    • FloatingActionButton
RaisedButton
  • RaisedButton是帶有一定圓角和陰影以及灰色背景的按鈕扶踊,并且在點(diǎn)擊的時(shí)候有動(dòng)畫效果泄鹏,其構(gòu)造函數(shù)如下:
class RaisedButton extends MaterialButton {
  /// Create a filled button.
  ///
  /// The [autofocus] and [clipBehavior] arguments must not be null.
  /// Additionally,  [elevation], [hoverElevation], [focusElevation],
  /// [highlightElevation], and [disabledElevation] must be non-negative, if
  /// specified.
  const RaisedButton({
    Key key,
    @required VoidCallback onPressed,
    VoidCallback onLongPress,
    ValueChanged<bool> onHighlightChanged,
    MouseCursor mouseCursor,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color focusColor,
    Color hoverColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    double elevation,
    double focusElevation,
    double hoverElevation,
    double highlightElevation,
    double disabledElevation,
    EdgeInsetsGeometry padding,
    VisualDensity visualDensity,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    FocusNode focusNode,
    bool autofocus = false,
    MaterialTapTargetSize materialTapTargetSize,
    Duration animationDuration,
    Widget child,
  })
FlatButton
  • FlatButton:默認(rèn)沒有背景顏色,不帶陰影秧耗,高亮狀態(tài)下有背景备籽,構(gòu)造函數(shù)如下:
class FlatButton extends MaterialButton {
  /// Create a simple text button.
  ///
  /// The [autofocus] and [clipBehavior] arguments must not be null.
  const FlatButton({
    Key key,
    @required VoidCallback onPressed,
    VoidCallback onLongPress,
    ValueChanged<bool> onHighlightChanged,
    MouseCursor mouseCursor,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color focusColor,
    Color hoverColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    EdgeInsetsGeometry padding,
    VisualDensity visualDensity,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    FocusNode focusNode,
    bool autofocus = false,
    MaterialTapTargetSize materialTapTargetSize,
    @required Widget child,
  })
OutlineButton
  • OutlineButton:帶有邊框的按鈕,構(gòu)造函數(shù)如下:
class OutlineButton extends MaterialButton {
  /// Create an outline button.
  ///
  /// The [highlightElevation] argument must be null or a positive value
  /// and the [autofocus] and [clipBehavior] arguments must not be null.
  const OutlineButton({
    Key key,
    @required VoidCallback onPressed,
    VoidCallback onLongPress,
    MouseCursor mouseCursor,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color focusColor,
    Color hoverColor,
    Color highlightColor,
    Color splashColor,
    double highlightElevation,
    this.borderSide,
    this.disabledBorderColor,
    this.highlightedBorderColor,
    EdgeInsetsGeometry padding,
    VisualDensity visualDensity,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    FocusNode focusNode,
    bool autofocus = false,
    Widget child,
  })
FloatingActionButton
  • FloatingActionButton:懸浮按鈕分井,構(gòu)造函數(shù)如下:
class FloatingActionButton extends StatelessWidget {
  /// Creates a circular floating action button.
  ///
  /// The [mini] and [clipBehavior] arguments must not be null. Additionally,
  /// [elevation], [highlightElevation], and [disabledElevation] (if specified)
  /// must be non-negative.
  const FloatingActionButton({
    Key key,
    this.child,
    this.tooltip,
    this.foregroundColor,
    this.backgroundColor,
    this.focusColor,
    this.hoverColor,
    this.splashColor,
    this.heroTag = const _DefaultHeroTag(),
    this.elevation,
    this.focusElevation,
    this.hoverElevation,
    this.highlightElevation,
    this.disabledElevation,
    @required this.onPressed,
    this.mouseCursor,
    this.mini = false,
    this.shape,
    this.clipBehavior = Clip.none,
    this.focusNode,
    this.autofocus = false,
    this.materialTapTargetSize,
    this.isExtended = false,
  })
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return ButtonWidget();
  }
}

class ButtonWidget extends StatelessWidget {
  const ButtonWidget({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        RaisedButton(
          child: Text("RaisedButton"),
          textColor: Colors.white,
          onPressed: (){
            print("RaisedButton click");
          },
        ),
        FlatButton(
          child: Text("FlatButton"),
          onPressed: (){
            print("FlatButton click");
          },
        ),
        OutlineButton(
          child: Text("OutlineButton"),
          onPressed: (){
            print("OutlineButton click");
          },
        ),
        FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: (){
            print("FloatingActionButton click");
          },
        ),
        //自定義button
        FlatButton(
          color: Colors.green,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8)
          ),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(Icons.favorite,color: Colors.red),
              Text("喜歡作者")
            ],
          ),
          onPressed: (){
            print("FlatButton");
          },
        )
      ],
    );
  }
}
  • 執(zhí)行結(jié)果如下圖所示:
image.png
  • ShapeBorder:設(shè)置邊框车猬,是一個(gè)抽象類,其常用子類為RoundedRectangleBorder
  • 1.默認(rèn)情況下Button上下會(huì)有一定的間距尺锚,可通過materialTapTargetSize屬性珠闰,去除間距;
  • 2.Button在沒有文本的情況下瘫辩,會(huì)顯示默認(rèn)尺寸大小伏嗜,可通過ButtonTheme屬性,自定義任意尺寸大蟹パ帷承绸;
  • 3.可通過padding屬性,去除內(nèi)容邊距挣轨;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

//`ButtonTheme`屬性军熏,自定義任意尺寸大小
//通過`padding`屬性,去除內(nèi)容邊距
class _SFHomeContentState extends State<SFHomeContent>{
  final imageUrl = "";
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return Column(
      children: [
        ButtonTheme(
          minWidth: 30,
          height: 15,
          child: FlatButton(
            color: Colors.red,
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
            child: Text("FlatButton"),
            textColor: Colors.white,
            padding: EdgeInsets.all(0),
            onPressed: (){
              print("click FlatButton");
            },
          ),
        )
      ],
    );
  }
}

class ButtonExtra01 extends StatelessWidget {
  const ButtonExtra01({
    Key key,
  }) : super(key: key);
  //`materialTapTargetSize`屬性刃唐,去除間距
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        FlatButton(
          color: Colors.red,
          child: Text("FlatButton"),
          materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          textColor: Colors.white,
          onPressed: (){
            print("click FlatButton");
          },
        ),
        FlatButton(
          color: Colors.red,
          child: Text("FlatButton"),
          materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          textColor: Colors.white,
          onPressed: (){
            print("click FlatButton");
          },
        )
      ],
    );
  }
}
圖片Widget
  • Image:圖片組件,其構(gòu)造函數(shù)如下:
class Image extends StatefulWidget {
  /// Creates a widget that displays an image.
  ///
  /// To show an image from the network or from an asset bundle, consider using
  /// [new Image.network] and [new Image.asset] respectively.
  ///
  /// The [image], [alignment], [repeat], and [matchTextDirection] arguments
  /// must not be null.
  ///
  /// Either the [width] and [height] arguments should be specified, or the
  /// widget should be placed in a context that sets tight layout constraints.
  /// Otherwise, the image dimensions will change as the image is loaded, which
  /// will result in ugly layout changes.
  ///
  /// Use [filterQuality] to change the quality when scaling an image.
  /// Use the [FilterQuality.low] quality setting to scale the image,
  /// which corresponds to bilinear interpolation, rather than the default
  /// [FilterQuality.none] which corresponds to nearest-neighbor.
  ///
  /// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
  const Image({
    Key key,
    @required this.image,
    this.frameBuilder,
    this.loadingBuilder,
    this.errorBuilder,
    this.semanticLabel,
    this.excludeFromSemantics = false,
    this.width,
    this.height,
    this.color,
    this.colorBlendMode,
    this.fit,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.centerSlice,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
    this.isAntiAlias = false,
    this.filterQuality = FilterQuality.low,
  })
  • @required this.image:必選參數(shù)界轩,參數(shù)類型為ImageProvider是一個(gè)抽象類画饥,常見子類有:NetworkImageAssetImage
NetworkImage加載網(wǎng)絡(luò)圖片
  • 案例代碼如下:
class NetWorkImage extends StatelessWidget {
  const NetWorkImage({
    Key key,
    @required this.imageUrl,
  }) : super(key: key);

  final String imageUrl;

  @override
  Widget build(BuildContext context) {
    return Image(
      image: NetworkImage(imageUrl),
      width: 200,
      height: 200,
      fit: BoxFit.fill, 
      alignment: Alignment.bottomLeft,
      repeat: ImageRepeat.repeatY,
    );
  }
}
  • fit:圖片的顯示模式,等價(jià)于OC中的contentMode
  • alignment:圖片的對(duì)齊方式浊猾;
  • repeat:當(dāng)圖片未填充滿控件時(shí)抖甘,可設(shè)置重復(fù)填充;
AssetImage加載本地圖片
  • 首先在Flutter項(xiàng)目中引入圖片資源葫慎;
  • 然后在pub spec.yaml文件中進(jìn)行配置衔彻,然后執(zhí)行flutter pub get命令薇宠;
  • 最后使用圖片;
  • 流程如下:
image.png
  • 代碼案例:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent>{
  final imageUrl = "";
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return Image(
      image: AssetImage("asset/images/180.png"),
    );
  }
}
  • 案例代碼:
class ImageExtra extends StatelessWidget {
  const ImageExtra({
    Key key,
    @required this.imageUrl,
  }) : super(key: key);

  final String imageUrl;

  @override
  Widget build(BuildContext context) {
    return FadeInImage(
      placeholder: AssetImage("asset/images/180.png"), //設(shè)置占位圖
      image: NetworkImage(imageUrl),//設(shè)置網(wǎng)絡(luò)圖片
      fadeOutDuration: Duration(milliseconds: 1),//淡入淡出的動(dòng)畫效果
      fadeInDuration: Duration(milliseconds: 1),
    );
  }
}
  • placeholder屬性可設(shè)置占位圖片艰额;
Icon圖標(biāo)
  • 1.Icon可設(shè)置字體圖片與圖片圖標(biāo)澄港;
  • 2.字體圖片與圖片圖標(biāo)是矢量圖,放大不會(huì)失真柄沮;
  • 3.圖標(biāo)可設(shè)置顏色回梧;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}


class _SFHomeContentState extends State<SFHomeContent>{
  final imageUrl = "";
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    //1.Icon可設(shè)置字體圖片與圖片圖標(biāo)
    //2.字體圖片與圖片圖標(biāo)是矢量圖,放大不會(huì)失真
    //3.圖標(biāo)可設(shè)置顏色
    //4.下面三種寫法等價(jià)
    return Icon(Icons.pets,size: 200,color: Colors.red);
    // return Icon(IconData(0xe91d, fontFamily: 'MaterialIcons'),size: 200,color: Colors.red);
    // return Text("\ue91d",style: TextStyle(fontSize: 100,fontFamily: 'MaterialIcons',color: Colors.red));
  }
}
輸入框Widget -- TextField
  • TextField的構(gòu)造函數(shù):
class TextField extends StatefulWidget {
  /// Creates a Material Design text field.
  ///
  /// If [decoration] is non-null (which is the default), the text field requires
  /// one of its ancestors to be a [Material] widget.
  ///
  /// To remove the decoration entirely (including the extra padding introduced
  /// by the decoration to save space for the labels), set the [decoration] to
  /// null.
  ///
  /// The [maxLines] property can be set to null to remove the restriction on
  /// the number of lines. By default, it is one, meaning this is a single-line
  /// text field. [maxLines] must not be zero.
  ///
  /// The [maxLength] property is set to null by default, which means the
  /// number of characters allowed in the text field is not restricted. If
  /// [maxLength] is set a character counter will be displayed below the
  /// field showing how many characters have been entered. If the value is
  /// set to a positive integer it will also display the maximum allowed
  /// number of characters to be entered.  If the value is set to
  /// [TextField.noMaxLength] then only the current length is displayed.
  ///
  /// After [maxLength] characters have been input, additional input
  /// is ignored, unless [maxLengthEnforced] is set to false. The text field
  /// enforces the length with a [LengthLimitingTextInputFormatter], which is
  /// evaluated after the supplied [inputFormatters], if any. The [maxLength]
  /// value must be either null or greater than zero.
  ///
  /// If [maxLengthEnforced] is set to false, then more than [maxLength]
  /// characters may be entered, and the error counter and divider will
  /// switch to the [decoration.errorStyle] when the limit is exceeded.
  ///
  /// The text cursor is not shown if [showCursor] is false or if [showCursor]
  /// is null (the default) and [readOnly] is true.
  ///
  /// The [selectionHeightStyle] and [selectionWidthStyle] properties allow
  /// changing the shape of the selection highlighting. These properties default
  /// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and
  /// must not be null.
  ///
  /// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect],
  /// [maxLengthEnforced], [scrollPadding], [maxLines], [maxLength],
  /// [selectionHeightStyle], [selectionWidthStyle], and [enableSuggestions]
  /// arguments must not be null.
  ///
  /// See also:
  ///
  ///  * [maxLength], which discusses the precise meaning of "number of
  ///    characters" and how it may differ from the intuitive meaning.
  const TextField({
    Key key,
    this.controller,
    this.focusNode,
    this.decoration = const InputDecoration(),
    TextInputType keyboardType,
    this.textInputAction,
    this.textCapitalization = TextCapitalization.none,
    this.style,
    this.strutStyle,
    this.textAlign = TextAlign.start,
    this.textAlignVertical,
    this.textDirection,
    this.readOnly = false,
    ToolbarOptions toolbarOptions,
    this.showCursor,
    this.autofocus = false,
    this.obscuringCharacter = '?',
    this.obscureText = false,
    this.autocorrect = true,
    SmartDashesType smartDashesType,
    SmartQuotesType smartQuotesType,
    this.enableSuggestions = true,
    this.maxLines = 1,
    this.minLines,
    this.expands = false,
    this.maxLength,
    this.maxLengthEnforced = true,
    this.onChanged,
    this.onEditingComplete,
    this.onSubmitted,
    this.inputFormatters,
    this.enabled,
    this.cursorWidth = 2.0,
    this.cursorRadius,
    this.cursorColor,
    this.selectionHeightStyle = ui.BoxHeightStyle.tight,
    this.selectionWidthStyle = ui.BoxWidthStyle.tight,
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.dragStartBehavior = DragStartBehavior.start,
    this.enableInteractiveSelection = true,
    this.onTap,
    this.mouseCursor,
    this.buildCounter,
    this.scrollController,
    this.scrollPhysics,
    this.autofillHints,
  })
  • 案例代碼如下:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage()
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基礎(chǔ)widget")
      ),
      body: SFHomeContent()
    );
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}


class _SFHomeContentState extends State<SFHomeContent>{
  final usernameTextEditController = TextEditingController();
  final passwordTextEditController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return Theme(
      data: ThemeData(
        primaryColor: Colors.red
      ),
      child: Padding(
        padding: EdgeInsets.all(8.0),
        child:Column(
          children: [
            TextField(
              controller: usernameTextEditController,
              decoration: InputDecoration(
                labelText: "username",
                icon: Icon(Icons.people),
                hintText: "請(qǐng)輸入用戶名",
                border: OutlineInputBorder(),
                filled: true,
                fillColor: Colors.red[100],
              ),
              onChanged: (value){
                print("onChanged:$value");
              },
              onSubmitted: (value){
                print("onSubmitted:$value");
              },
            ),
            SizedBox(height: 10),
            TextField(
              controller: passwordTextEditController,
              decoration: InputDecoration(
                labelText: "password",
                icon: Icon(Icons.lock),
                hintText: "請(qǐng)輸入密碼",
                border: InputBorder.none,
                filled: true,
                fillColor: Colors.red[100],
              ),
              onChanged: (value){
                print("onChanged:$value");
              },
              onSubmitted: (value){
                print("onSubmitted:$value");
              },
            ),
            SizedBox(height: 20),
            Container(
              width: 300,
              height: 35,
              child: FlatButton(
                child: Text("登 錄",style: TextStyle(fontSize: 20,color: Colors.white)),
                color: Colors.blue,
                onPressed: (){
                  print("login");
                  final username = usernameTextEditController.text;
                  final password = passwordTextEditController.text;
                  print("username = $username,password = $password");
                },
              ),
            )
          ],
        )
      )
    );
  }
}
  • 運(yùn)行結(jié)果如下:
image.png
  • Theme:設(shè)置整個(gè)UI的主題風(fēng)格祖搓;
  • 給目標(biāo)組件設(shè)置寬高狱意,通常是在目標(biāo)組件外面再包裝一層Container,然后設(shè)置widthheight屬性拯欧;
  • onChanged:監(jiān)聽輸入框文本變化的回調(diào)详囤;
  • onSubmitted:監(jiān)聽輸入框提交時(shí)的回調(diào);
  • 在登錄按鈕的點(diǎn)擊回調(diào)中镐作,獲取輸入框的文本是通過給輸入框綁定一個(gè)TextEditingController藏姐,然后通過TextEditingController.text獲取輸入框的文本;
單子布局組件
  • 常見的單子布局組件有:Align滑肉,Center包各,Padding,Container
Align組件
  • Align:即對(duì)齊;
  • Center組件:是繼承自Align組件的靶庙,本質(zhì)上Align與Center等價(jià)问畅;
  • 案例代碼如下:
class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");

    return Container(
      color: Colors.red,
      child: Align(
        child: Icon(Icons.pets,size: 50),
        alignment: Alignment(0,0),
        widthFactor: 5,
        heightFactor: 3,
      ),
    );
  }
}
  • Align默認(rèn)占據(jù)整個(gè)屏幕大小六荒;
  • widthFactor與heightFactor 是child的尺寸的倍數(shù)大小 作為Align的尺寸大谢つ贰;
Padding組件
  • Padding:主要用來設(shè)置子Widget到父Widget的邊距掏击;
  • 其構(gòu)造函數(shù):
const Padding({
    Key key,
    @required this.padding,
    Widget child,
  })
  • 案例代碼:
class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    print("_SFHomeContentState build");
    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.only(bottom: 5),
          child: Text("日你啊滿滿的個(gè)單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
        ),
        Padding(
          padding: const EdgeInsets.only(bottom: 5),
          child: Text("日你啊滿滿的個(gè)單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
        ),
        Text("日你啊滿滿的個(gè)單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red))
      ],
    );
  }
}
Container組件
  • Container:顧名思義為容器組件卵皂,
  • 其構(gòu)造函數(shù)如下:
Container({
    Key key,
    this.alignment,
    this.padding,
    this.color,
    this.decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
    this.child,
    this.clipBehavior = Clip.none,
  })
  • 案例代碼:
class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    return Container(
      // color: Colors.red,
      width: 200,
      height: 200,
      child: Text("這是一個(gè)文本",style: TextStyle(color: Colors.white),),
      alignment: Alignment(0,0),
      padding: EdgeInsets.all(20),
      margin: EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Colors.red,
        border: Border.all(
          width: 5,
          color: Colors.purple
        ),
        borderRadius: BorderRadius.circular(100),
        boxShadow: [
          BoxShadow(color: Colors.orange,offset: Offset(10,10),spreadRadius: 5,blurRadius: 10),
          BoxShadow(color: Colors.blue,offset: Offset(-10,10),spreadRadius: 5,blurRadius: 10)
        ]
      ),
    );
  }
}
  • padding:設(shè)置容器內(nèi)間距;
  • margin:設(shè)置容器外間距砚亭;
  • decoration:設(shè)置裝飾灯变,內(nèi)部包含邊框,圓角捅膘,陰影等等添祸;
  • 效果圖:
image.png
多子布局組件
  • 常見的多子布局組件有:Flex,Row寻仗,Column
Flex組件
  • Row組件與Column組件都是繼承自Flex組件刃泌;
  • 主軸:mainAxis即主方向的軸;
  • 交叉軸:crosssAxis
Row組件
  • Row的主軸方向?yàn)椋核椒较颍徊孑S為豎直方向耙替;

  • Row在主軸方向即水平方向占據(jù)比較大的空間亚侠;

    • 若在水平方向上希望包裹內(nèi)容,那么設(shè)置mainAxisSize = min俗扇;
  • Row在交叉軸方向即豎直方向包裹內(nèi)容硝烂;

  • mainAxisAlignment:主軸方向上的對(duì)齊方式

    • start:主軸的開始位置依次擺放元素;
    • end:主軸的結(jié)束位置依次擺放元素狐援;
    • center:主軸中心點(diǎn)對(duì)齊钢坦;
    • spaceBetween:左右間距為0,其他元素之間間距平分啥酱;
    • spaceAround:左右間距是其他元素之間間距的一半爹凹;
    • spaceEvenly:所有間距平分;
  • crossAxisAlignment:交叉方向上的對(duì)齊方式

    • start:交叉軸的開始位置依次擺放元素镶殷;
    • end:交叉軸的結(jié)束位置依次擺放元素禾酱;
    • center:交叉軸中心點(diǎn)對(duì)齊;
    • textBaseline:基線對(duì)齊(必須有文本內(nèi)容才有效果)绘趋;
    • stretch:將所有子元素颤陶,拉伸到最大;
  • 案例代碼:mainAxisSize = min

class _SFHomeContentState extends State<SFHomeContent>{
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.bug_report),
          Text("bug")
        ],
      ),
    );
  }
}
  • 案例代碼:
class RowDemo extends StatelessWidget {
  const RowDemo({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.center,
      textBaseline: TextBaseline.alphabetic,
      children: [
        Container(width: 80,height: 60,color: Colors.red),
        Container(width: 80,height: 80,color: Colors.green),
        Container(width: 80,height: 100,color: Colors.blue),
        Container(width: 80,height: 120,color: Colors.orange),
      ],
    );
  }
}
Flexible與Expanded
  • Flexible:空間拉伸與收縮陷遮,占滿空間滓走;
  • Expanded:空間拉伸與收縮,占滿空間帽馋,與Flexible效果等價(jià)搅方,區(qū)別在與使用Expanded不用再設(shè)置fit: FlexFit.tight,因?yàn)?code>Expanded在內(nèi)部已經(jīng)設(shè)置了绽族,所以其使用更廣姨涡;
  • 空間剩余時(shí),會(huì)進(jìn)行拉伸占滿剩余空間吧慢;
  • 空間不足時(shí)涛漂,會(huì)收縮子widget,占滿空間检诗;
class RowDemo2 extends StatelessWidget {
  const RowDemo2({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      textBaseline: TextBaseline.alphabetic,
      children: [
        Flexible(
          child: Container(width: 80,height: 60,color: Colors.red),
          fit: FlexFit.tight,
        ),
        Expanded(
          child: Container(width: 80,height: 80,color: Colors.green),
        ),
        Container(width: 80,height: 100,color: Colors.blue),
        Container(width: 80,height: 120,color: Colors.orange),
      ],
    );
  }
}

這里補(bǔ)充個(gè)小知識(shí)點(diǎn): 如果children中出現(xiàn)2個(gè)Flexible或者Expand匈仗,那么剩余空間會(huì)根據(jù)兩個(gè)Expand的Flex比例來填充,如果沒有設(shè)置Flex逢慌,那么默認(rèn)的Flex值為1

Column組件
  • Column的主軸方向?yàn)椋贺Q直方向悠轩,交叉軸為水平方向;
  • 與Row組件類似涕癣,只是子組件在主軸方向即豎直方向上排列哗蜈;
Stack組件
  • 在開發(fā)中,我們多個(gè)組件可能需要重疊顯示坠韩,在Android中使用Frame布局實(shí)現(xiàn)距潘,而在Flutter中我們需要使用層疊布局Stack來實(shí)現(xiàn);
  • 內(nèi)部子組件默認(rèn)從左上角開始排布只搁;
  • Stack默認(rèn)的大小是包裹內(nèi)容的尺寸大幸舯取;
  • 參數(shù)alignment:表示從什么位置開始排布所有的子組件氢惋;
  • 參數(shù)fit:expand表示將所有子組件拉伸到最大洞翩;
  • 參數(shù)overflow:對(duì)于超出父組件區(qū)域的子組件的處理
    - flip:超出部分被裁減;
    - visible:超出部分依然顯示焰望;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent> {
  @override
  Widget build(BuildContext context) {
    return StackDemo1();
  }
}

class StackDemo2 extends StatelessWidget {
  const StackDemo2({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
            height: 200,
            child: Image.asset("asset/images/180.png", fit: BoxFit.fill),
            width: double.infinity),
        Positioned(
          child: Container(
            child: Row(
              children: [
                Text("這是一行文本", style: TextStyle(fontSize: 17, color: Colors.white)),
                IconButton(icon: Icon(Icons.favorite),color: Colors.white,onPressed: (){
                })
              ],
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
            ),
            color: Color.fromARGB(150, 0, 0, 0),
            width: double.infinity,
            padding: EdgeInsets.symmetric(horizontal: 8),
          ),
          left: 0,
          right: 0,
          bottom: 0,
        )
      ],
    );
  }
}

class StackDemo1 extends StatelessWidget {
  const StackDemo1({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Image.asset("asset/images/180.png"),
        Positioned(
          child: Container(
            width: 100,
            height: 100,
            color: Colors.green,
          ),
          right: 0,
        ),
        Positioned(
          child: Text("推客圖標(biāo)", style: TextStyle(fontSize: 20)),
          left: 0,
        )
      ],
      alignment: AlignmentDirectional.bottomStart,
      overflow: Overflow.visible,
    );
  }
}
  • StackDemo1與StackDemo2的效果圖如下:
image.png
image.png
  • 滾動(dòng)組件有ListView骚亿,GridViewSliver

ListView

  • ListView創(chuàng)建的方式通常有三種熊赖,分別為ListView()来屠,ListView.builder()ListView.separated()
ListView創(chuàng)建方式
  • 第一種方式:ListView()
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
      scrollDirection: Axis.vertical,
      itemExtent: 100,//設(shè)置Item的高度
      children: List.generate(100, (index) {
        return ListTile(
          leading: Icon(Icons.people),
          trailing: Icon(Icons.delete),
          title: Text("聯(lián)系人${index+1}"),
          subtitle: Text("聯(lián)系人電話號(hào)碼:19991604555"),
        );
      }),
    );
  }
}
  • ListTile組件就是ListView的Item震鹉;
  • itemExtent:設(shè)置Item的高度俱笛;
  • 效果圖如下:
image.png
  • 通過ListView()創(chuàng)建,會(huì)一次性創(chuàng)建100個(gè)Item传趾,這樣性能比較差迎膜,其適用于Item個(gè)數(shù)確定,且數(shù)量較少的情況下才會(huì)采用浆兰;

  • 第二種方式:ListView.builder()

  • ListView.builder()不會(huì)一次性創(chuàng)建所有Item磕仅,而是需要展示的Item才會(huì)去創(chuàng)建,性能較好镊讼;

  • 案例代碼如下:

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        itemCount: 100,
        itemExtent: 50,
        itemBuilder: (BuildContext ctx, int index){
          return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
    }
    );
  }
}
  • 第三種方式: ListView.separated()
  • 案例代碼如下:
class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.separated(
        itemBuilder: (BuildContext ctx, int index){
          return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
        },
        //分割線
        separatorBuilder: (BuildContext ctx,int index){
          return Divider(color: Colors.red,indent: 20,endIndent: 20,thickness: 5);
        },
        itemCount: 100
    );
  }
}
  • itemBuilder:創(chuàng)建Item宽涌;
  • separatorBuilder:創(chuàng)建分割線;

GridView

  • GridView的創(chuàng)建方式有:GridView()蝶棋,GridView.builder()
  • 案例代碼一:GridView()+ SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        childAspectRatio: 1.5,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8
      ),
      children:
        List.generate(100, (index) {
           return Container(
             color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
           );
        }),
    );
  }
}
  • SliverGridDelegateWithFixedCrossAxisCount:交叉軸方向上item數(shù)量固定卸亮,其寬度根據(jù)屏幕的寬度與item的數(shù)量進(jìn)行計(jì)算;

    • crossAxisCount:item的個(gè)數(shù)玩裙;
    • childAspectRatio:item的寬高比兼贸;
    • crossAxisSpacing:交叉軸方向上 item之間的間距;
    • mainAxisSpacing:主軸方向上 item之間的間距吃溅;
  • 案例代碼二:GridView()+ SliverGridDelegateWithMaxCrossAxisExtent

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView(
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 220,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
        childAspectRatio: 1.5
      ),
      children: List.generate(100, (index) {
        return Container(
          color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
        );
      })
    );
  }
}
  • SliverGridDelegateWithMaxCrossAxisExtent:交叉軸方向上的設(shè)置item寬度溶诞,個(gè)數(shù)不固定;

    • maxCrossAxisExtent:item的最大寬度决侈;
  • 案例代碼三:GridView.builder() + SliverGridDelegateWithFixedCrossAxisCount

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          crossAxisSpacing: 8,
          mainAxisSpacing: 8
        ),
        itemBuilder: (BuildContext ctx,int index){
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
          );
        }
    );
  }
}

Slivers -- CustomScrollView

  • CustomScrollView:自定義滾動(dòng)組件螺垢,需要傳入Slivers即Sliver數(shù)組,我們知道ListViewGridView都是繼承自BoxScrollView,而BoxScrollView是一個(gè)抽象類枉圃,從源碼來看ListViewGridView在察創(chuàng)建的過程中都需要執(zhí)行buildSlivers方法功茴,其內(nèi)部調(diào)用buildChildLayout方法,這是一個(gè)抽象方法孽亲,分別由ListViewGridView來實(shí)現(xiàn)坎穿,最終提供一個(gè)Sliver數(shù)組,其中ListView提供的Sliver為SliverFixedExtentList返劲,GridView提供的Sliver為SliverGrid

  • 案例代碼:單個(gè)Sliver

import 'dart:math';
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基礎(chǔ)widget")),
        body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverDemo1();
  }
}

class SliverDemo1 extends StatelessWidget {
  const SliverDemo1({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverSafeArea(
          sliver: SliverPadding(
            padding: EdgeInsets.all(8),
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext ctx,int index){
                    return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
                    );
                  },
                childCount: 100
              ),
            ),
          ),
        )
      ],
    );
  }
}
  • 自定義CustomScrollView玲昧,需傳入Slivers數(shù)組,這里傳入的Sliver為SliverGrid篮绿;

  • 參數(shù)gridDelegate:是提供布局信息孵延;

  • 參數(shù)delegate:是提供item組件,類型為SliverChildDelegate亲配;

  • SliverChildDelegate是抽象類隙袁,其作用是用來創(chuàng)建滾動(dòng)組件的item,其有兩個(gè)子類分別為SliverChildListDelegateSliverChildBuilderDelegate

    • SliverChildListDelegate:性能較差弃榨,item一次性創(chuàng)建所有菩收;
    • SliverChildBuilderDelegate:性能較好,創(chuàng)建需要展示的item鲸睛;
  • SafeAreaSliverSafeArea的區(qū)別:

    • SafeArea:安全區(qū)域娜饵,讓目標(biāo)組件在安全區(qū)域內(nèi)顯示;
    • SliverSafeArea:給Sliver設(shè)置安全區(qū)域官辈,且在滾動(dòng)時(shí)可以在非安全區(qū)域內(nèi)滾動(dòng)箱舞,而SafeArea不可以;
  • SliverPadding:是Sliver自己的設(shè)置內(nèi)邊距的組件拳亿;

  • 案例代碼:多個(gè)Sliver

import 'dart:math';
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // appBar: AppBar(title: Text("基礎(chǔ)widget")),
        body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
          pinned: true,
          expandedHeight: 200,
          flexibleSpace: FlexibleSpaceBar(
            title: Text("Hello World!!",style: TextStyle(fontSize: 25),),
          ),
        ),
        SliverGrid(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 2.5
          ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index){
                  return Container(
                    color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
                  );
                },
                childCount: 10
            ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
              (BuildContext ctx,int index){
                return ListTile(
                  leading: Icon(Icons.people),
                  title: Text("聯(lián)系人$index"),
                );
              },
            childCount: 20
          ),
        )
      ],
    );
  }
}
  • slivers數(shù)組中傳入了SliverAppBar晴股,SliverGridSliverList三種類型的Sliver,效果如下:
    image.png

滾動(dòng)組件的監(jiān)聽

  • 滾動(dòng)組件的監(jiān)聽通常有兩種方式肺魁,分別為controllerNotificationListener
controller監(jiān)聽
  • 可以設(shè)置默認(rèn)值offset电湘;
  • 監(jiān)聽滾動(dòng),也可以監(jiān)聽滾動(dòng)的位置鹅经;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatefulWidget {
  @override
  _SFHomePageState createState() => _SFHomePageState();
}

class _SFHomePageState extends State<SFHomePage> {
  ScrollController controller = ScrollController(initialScrollOffset: 300);
  bool isShowFloatButton = false;

  @override
  void initState() {
    super.initState();
    controller.addListener(() {
      print("監(jiān)聽到滾動(dòng): ${controller.offset}");
      setState(() {
        isShowFloatButton = controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("基礎(chǔ)widget")),
      body: SFHomeContent(controller),
      floatingActionButton: isShowFloatButton ? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: (){
          controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

class SFHomeContent extends StatelessWidget {
  final ScrollController controller;
  SFHomeContent(this.controller);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: controller,
      itemBuilder: (BuildContext ctx, int index) {
        return ListTile(
          leading: Icon(Icons.people),
          title: Text("聯(lián)系人$index"),
        );
      },
      itemCount: 100,
    );
  }
}
  • 右下角懸浮按鈕寂呛,當(dāng)前滾動(dòng)偏移量>=1000時(shí)顯示;
NotificationListener監(jiān)聽
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatefulWidget {
  @override
  _SFHomePageState createState() => _SFHomePageState();
}

class _SFHomePageState extends State<SFHomePage> {
  bool isShowFloatButton = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("基礎(chǔ)widget")),
      body: SFHomeContent(),
      floatingActionButton: isShowFloatButton
          ? FloatingActionButton(
              child: Icon(Icons.arrow_upward),
              onPressed: () {

              },
            )
          : null,
    );
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (ScrollNotification notification){
        if(notification is ScrollStartNotification){
          print("開始滾動(dòng)");
        }else if (notification is ScrollUpdateNotification){
          print("正在滾動(dòng) -- 總區(qū)域:${notification.metrics.maxScrollExtent} 當(dāng)前位置: ${notification.metrics.pixels}");
        }else if (notification is ScrollEndNotification){
          print("結(jié)束滾動(dòng)");

        }

        return true;
      },
      child: ListView.builder(
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("聯(lián)系人$index"),
          );
        },
        itemCount: 100,
      ),
    );
  }
}

總結(jié)

  • 現(xiàn)在對(duì)以上出現(xiàn)的所有Widget做一個(gè)總結(jié)瘾晃,主要針對(duì)繼承關(guān)系:

  • StatelessWidget為中心的Widget組件有如下:

    image.png

  • StatefulWidget為中心的Widget組件有如下:

    image.png

  • RenderObjectWidget為中心的Widget組件有如下:

    image.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贷痪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蹦误,更是在濱河造成了極大的恐慌劫拢,老刑警劉巖肉津,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異舱沧,居然都是意外死亡阀圾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門狗唉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涡真,你說我怎么就攤上這事分俯。” “怎么了哆料?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵缸剪,是天一觀的道長。 經(jīng)常有香客問我东亦,道長杏节,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任典阵,我火速辦了婚禮奋渔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壮啊。我一直安慰自己嫉鲸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布歹啼。 她就那樣靜靜地躺著玄渗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狸眼。 梳的紋絲不亂的頭發(fā)上藤树,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音拓萌,去河邊找鬼岁钓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛微王,可吹牛的內(nèi)容都是我干的甜紫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼骂远,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼囚霸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起激才,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤拓型,失蹤者是張志新(化名)和其女友劉穎额嘿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劣挫,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡册养,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了压固。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片球拦。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帐我,靈堂內(nèi)的尸體忽然破棺而出忠烛,到底是詐尸還是另有隱情扒袖,我是刑警寧澤旅东,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布毅往,位于F島的核電站,受9級(jí)特大地震影響芬为,放射性物質(zhì)發(fā)生泄漏萄金。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一媚朦、第九天 我趴在偏房一處隱蔽的房頂上張望氧敢。 院中可真熱鬧,春花似錦询张、人聲如沸福稳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽的圆。三九已至,卻和暖如春半火,著一層夾襖步出監(jiān)牢的瞬間越妈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工钮糖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梅掠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓店归,卻偏偏與公主長得像阎抒,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子消痛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容