菜逼八寫Flutter(2) - layout Widget


Posted by TempuraEngineer on 2022-09-01

目錄


children型

Row

水平排列用,不能換行,超出容器的寬度會報錯,若要換行可用Wrap替代。不能scroll,超出容器的寬度會報錯,可ListView替代

Column

垂直排列用,不能換行,如果超出容器的寬度會報錯

Column會調整自身的寬度到和其children一樣,並且在沒有設置高度、父層設了constraints的情況下,它會調整自身高度到跟父層一樣

Wrap

跟Row和Column差在會換行

Flex

類似Row和Column,但是透過direction控制children的方向(水平、垂直)

不過官網建議如果知道要甚麼方向的排列就直接用Row或Column,如果只有1個child用Align或Center

If you know the main axis in advance, then consider using a Row (if it's horizontal) or Column (if it's vertical) instead, because that will be less verbose.

If you only have one child, then rather than using Flex, Row, or Column, consider using Align or Center to position the child.

Expanded

常用來當Row、Column、Flex的child,會佔滿螢幕的寬度

設定flex後就可以用比例的方式分配佔寬,類似css flex-grow

Creates a widget that expands a child of a [Row], [Column], or [Flex] so that the child fills the available space along the flex widget's main axis

Row( // 裝在Row裡的Expanded會水平排列
    children: [
      Expanded(
        flex: 2, // 20%
        child: Container(height: 50, color: Colors.red),
      ),
      Expanded(
        flex: 6, // 60%
        child: Container(height: 50, color: Colors.amber),
      ),
      Expanded(
        flex: 2, // 20%
        child: Container(height: 50, color: Colors.blue),
      )
  ],
)

Column( // 裝在Column裡的Expanded會垂直排列
  children: [
    Expanded(
      child: Row( // 在垂直排列的Expanded裡放Row,再在Row裡放Expanded
        children: [
          Expanded(
            child: Container(
              color: Colors.pink[100],
            ),
          ),
          Expanded(
            child: Container(color: Colors.green[100]),
          )
        ],
      ),
    ),
    Expanded(
      child: Row(
        children: [
          Expanded(
            child: Container(color: Colors.blue[100]),
          ),
          Expanded(
            child: Container(color: Colors.purple[100]),
          ),
        ],
      ),
    ),
)

可用來避免圖片太大超出畫面

Row(
    children: [
      Expanded(
        child: Image.network('https://images.unsplash.com/photo-1534637810397-1acee982a12e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=435&q=80'),
      ),
  ],
)

Flexible

和Expanded很像,只差在Flexible不會佔滿整個寬度

// // 寫在class _MyHomePageState裡

Expanded buildExpanded() {
  return Expanded(
      child: Container(
          child: const Text('expanded', style: TextStyle(fontSize: 24)),
          color: Colors.blue[100])
  );
}

Flexible buildFlexible() {
  return Flexible(
      child: Container(
          child: const Text('flexible', style: TextStyle(fontSize: 24)),
          color: Colors.pink[100])
  );
}

Column(
  children: <Widget>[
    Row(
      children: <Widget>[
        buildExpanded(),
        buildExpanded(),
      ],
    ),
    Row(
      children: <Widget>[
        buildExpanded(),
        buildFlexible(),
      ],
    ),
    Row(
      children: <Widget>[
        buildFlexible(),
        buildFlexible(),
      ],
    ),
  ],
)

Scaffold

Material提供的基本的排版widget,常當作一個頁面的開始

常見的有 AppBar、Bottom AppBar、bottomSheet的畫面可以用Scaffold排版

促略地說,可以將它想成有一格一格的餐盤,每一格都有規定只能放蔬菜、水果或肉

Scaffold(
  appBar: AppBar(
    title: Text(widget.title),
  ),
  body: Center(
      child: Text(
    'this is Text in Center',
    style: bodyColor,
  )),
  floatingActionButton: FloatingActionButton(
    onPressed: null, // 寫null不會觸發事件,如果要的話寫function名,或直接寫function
    tooltip: 'Increment',
    child: const Icon(Icons.add),
  ),
  // centerDocked至中定點,bottomBar、bottomSheet會凹下去,不想凹下去可用centerFloat
  floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 
  bottomNavigationBar: BottomAppBar(
    shape: const CircularNotchedRectangle(),
    child: Container(height: 50.0),
  ),
  bottomSheet: const TextField(
    decoration: InputDecoration(
      labelText: '搜尋',
      hintText: '搜尋文件',
      prefixIcon: Icon(Icons.search),
    ),
  ),
);

child型

Container

類似HTML的div。會根據child來調整它的寬高。沒有child的Container會盡其所能變大,除非它的父層有設了不限寬度的constraints給它,這樣的話Container就會縮到最小

Containers with no children try to be as big as possible unless the incoming constraints are unbounded, in which case they try to be as small as possible. Containers with children size themselves to their children.

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) { // 當狀態更新時會使用build重新繪製widget,回傳值是一個widget
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.purple,
        textTheme: const TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

Container(
  constraints: BoxConstraints.expand(
    // 設定給child的限制
    height: Theme.of(context).textTheme.headline1!.fontSize! +
        12, // 限制是高度為h1的大小 + 12,至於h1是參照該widget build()裡設定的theme.textTheme
  ),
  padding: const EdgeInsets.all(8.0), // 上下左右padding都8
  color: Colors.blue[600],
  alignment: Alignment
      .center, // 對齊,x軸有top、center、bottom,y軸有left、center、right,排列組合後共有9種
  transform: Matrix4.rotationZ(0.1), // 旋轉、扭曲、放大......等等特校
  child: Text(
    // 一個widget
    'Hello World',
    style: TextStyle(
        // 字的樣式
        color: Colors.white.withOpacity(0.5), // 字色為半透明白色
        fontSize:
            Theme.of(context).textTheme.headline1!.fontSize),
  ),
)

Container(
  height: 500,
  alignment: Alignment.center, // Container內的東西全都置中
  child: const Text( // 文字
    'this is container',
    // textAlign: TextAlign.center, // 水平置中
    style: TextStyle(
        color: Colors.white,
        fontSize: Theme.of(context).textTheme.headline6?.fontSize, // 也可以直接輸入數字。$h4是參照該widget build()裡設定的theme.textTheme
        fontWeight: FontWeight.bold),
  ),
  decoration: BoxDecoration( // 裝飾Container用
    color: Colors.blue[100], // 類似css background-color
    image: const DecorationImage( // 類似css background-image
      image: NetworkImage(
          'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
      fit: BoxFit.cover, // 類似css background-size:cover;
    ),
    border: Border.all( // 設置border的樣式
      color: Colors.blue,
      width: 8,
    ),
    borderRadius: BorderRadius.circular(12), // 設置四個角的border-radius,也可以只設定其中幾個角
  ),
)

Center

用來水平、垂直置中的widget。如果沒有設定widthFactor、heightFactor,它會盡其所能變大。如果有設則會是其child的倍數

if widthFactor is 2.0 then the width of this widget will always be twice its child's width.

// 放在Scaffold的body

Center(
  child: Container(
    height: 120,
    width: 120,
    decoration: BoxDecoration(
      gradient: LinearGradient(
          begin: Alignment.centerLeft,
          end: Alignment.centerRight,
          colors: <Color>[
        Colors.blue.shade200,
        Colors.blue.shade400,
        Colors.blue.shade600,
        Colors.indigo,
      ])),
  ),
)

Align

Align widget會對齊其child,並根據child改變自身大小

Center(
  child: Container(
    height: 200,
    width: 200,
    color: Colors.blue[50],
    child: const Align(
      alignment: Alignment.topRight,
      child: NetworkImage(
          'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
    ),
  ),
)

參考資料

Flutter widget index
Flutter for web developers

[Flutter] Layout Widget - Container
Flutter 常用組件講解 | ContainerWidget
Day 17 | Flutter的常用 widgets - Container、Row、Column

flutter Infinite Scrolling for ListView.builder


#Flutter #widget







Related Posts

[Note] 網頁的構成要素

[Note] 網頁的構成要素

改善肩背疼痛的一些動作

改善肩背疼痛的一些動作

七天打造自己的 Google Map 應用入門 - Day00

七天打造自己的 Google Map 應用入門 - Day00


Comments