iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

30天學習flutter系列 第 9

9.flutter的布局(二)

  • 分享至 

  • xImage
  •  

我們今天來用DartPaf來練習

嚴格布局約束

Ex1.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Container(width: 100, height: 100, color: Colors.red)
    );
  }
}

https://ithelp.ithome.com.tw/upload/images/20220924/20108931nhpYk5XjKe.png

我們能看Container

Container(
  color: Colors.red
),

Conainer的父級裝置屏幕,強制將Container變成和屏幕一樣大小

Ex2.

接著我們定義Container的大小

Container(width: 100, height: 100, color: Colors.red)

https://ithelp.ithome.com.tw/upload/images/20220924/201089311JLL9MIWZD.png

我們能看到Container想要變成100x100大小,但由於父widget強制約束他,使Container變成和屏幕相同大小

寬鬆布局約束

Ex1.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Center(
			  child: Container(width: 100, height: 100, color: Colors.red),
			)
    );
  }
}

https://ithelp.ithome.com.tw/upload/images/20220924/20108931ABPWPR4Jar.png

我們能看到螢幕強制Center與屏幕一樣大(嚴格約束),Center告訴Container可以變成任意大小,但不能超出屏幕(寬鬆約束)

Ex2.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Center(
			  child: Container(width: double.infinity, height: 100, color: Colors.red),
			)
    );
  }
}

我們試著讓Container的width變成無限大
Container(width: double.infinity, height: 100, color: Colors.red)

我們能看到,寬鬆布局約束讓Container不超過父widget
https://ithelp.ithome.com.tw/upload/images/20220924/20108931swRwdi6dTk.png

無邊界約束

Ex1.

我們接著用UnconstrainedBox來創建無邊界約束

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: UnconstrainedBox(
        child: Container(color: Colors.red, width: 5000, height: 50),
      )
    );
  }
}

UnconstrainedBox允許子級的Container可以變為任意大小。

由於是任意大小,我們這邊讓Container的寬度為5000像素。這遠遠超出超出我們的屏幕,以至於無法容納,所以UnconstrainedBox將顯示溢出警告(overflow warning)

UnconstrainedBox(
	child: Container(color: Colors.red, width: 5000, height: 50),
)

https://ithelp.ithome.com.tw/upload/images/20220924/20108931XPvKBljEQG.png

Ex2.

接著我們把UnconstrainedBox加入一個父widget

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Center(
        child: UnconstrainedBox(
          child: Container(color: Colors.red, width: 5000, height: 50),
        )
      )
    );
  }
}
Center(
	child: UnconstrainedBox(
	  child: Container(color: Colors.red, width: 5000, height: 50),
	)
)

我們能看到出現溢出警告,但範圍不同
https://ithelp.ithome.com.tw/upload/images/20220924/20108931MsE6gqVwfv.png

簡單的整理順序

1.裝置強制Center和裝置屏幕同樣大小,於是佔滿屏幕(嚴格布局約束)
2.Center允許UnconstrainedBox為不超過自己的任意大小(寬鬆布局約束)
3.UnconstrainedBox允許Container為任意大小(無邊界約束)

比較兩者溢出警告
Ex1的溢出警告是佔滿整個裝置屏幕,所以封鎖線是整個屏幕
Ex2的溢出警告則是超出我們Center,所以封鎖線是超出Center的範圍

Ex3.

接著我們使用OverflowBox

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: OverflowBox(
        minWidth: 0.0,
        minHeight: 0.0,
        maxWidth: double.infinity,
        maxHeight: double.infinity,
        child: Container(color: Colors.red, width: 5000, height: 50),
      )
    );
  }
}
OverflowBox(
	minWidth: 0.0,
	minHeight: 0.0,
	maxWidth: double.infinity,
	maxHeight: double.infinity,
	child: Container(color: Colors.red, width: 5000, height: 50),
)

屏幕強制OverflowBox變得和屏幕一樣大,然後OverflowBox允許其子widget設置為任意大小

OverflowBoxUnconstrainedBox類似,但不同的是,如果其子級超出該空間,它將不會顯示任何警告。

也就是在這種情況下,容器的寬度為5000像素,並且太大而無法容納在 OverflowBox 中,但是OverflowBox會全部顯示,而不會發出警告。

https://ithelp.ithome.com.tw/upload/images/20220924/201089314xEyPNNMlG.png


文本例子

Ex1.

使用FittedBox

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const FittedBox(
        child: Text('Example Text Test !!'),
      )
    );
  }
}
const FittedBox(
	child: Text('Example Text Test !!'),
)

我們的FittedBox被強制和屏幕一樣大,並且Text則是有一個自然寬度(intrinsic width),它取決於文本數量,字體大小等因素。FittedBox讓Text可以變為任意大小。

但是在Text告訴FittedBox 其大小後,FittedBox 縮放text直到填滿所有可用寬度(被嚴格布局約束,強制FittedBox大小,導致Text被縮放)

https://ithelp.ithome.com.tw/upload/images/20220924/20108931OdISfF6Pyv.png

Ex2.

接著我們在FittedBox的父widget使用Center

const Center(
  child: FittedBox(
    child: Text('Example Text Test !!'),
  ),
)

這裡Center將會讓FittedBox能夠變為不超過自己的任意大小。

FittedBox然後會根據Text調整自己的大小,然後讓 Text 可以變為所需的任意大小,由於二者俱有同一大小,因此不會發生縮放。(沒有被嚴格布局約束)

https://ithelp.ithome.com.tw/upload/images/20220924/20108931nGQAoVmLYZ.png

大量文字超出屏幕

Ex1.

修改Text文本來測試單Text太大超出範圍會發生甚麼

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const Center(
        child: FittedBox(
          child: Text(
              'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!'),
        ),
      )
    );
  }
}

我們知道FittedBox會嘗試根據Text大小調整大小,但不能大於widget的大小範圍。所以調整Text的大小以使其不超過屏幕
https://ithelp.ithome.com.tw/upload/images/20220924/201089312YH7lKeu3i.png

Ex2.

去掉FittedBox

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const Center(
          child: Text(
              'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!'),
      )
    );
  }
}

能看到Text則會從屏幕上獲取其最大寬度,並在合適的地方換行
https://ithelp.ithome.com.tw/upload/images/20220924/201089319odl22yI8a.png


常見的溢出警告

Ex1.Row

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Row(
        children: [
          Container(
            color: Colors.red,
            child: const Text(
              'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!',
            ),
          ),
          Container(color: Colors.blue, child: const Text('Hello Test!')),
        ],
      )
    );
  }
}
Row(
	children: [
	  Container(
		color: Colors.red,
		child: const Text(
		  'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!',
		),
	  ),
	  Container(color: Colors.blue, child: const Text('Hello Test!')),
	],
)

Row不會對其子級施加任何約束(無邊界約束),因此它的children有可能超出Row的可用寬度。當超出時,Row會和UnconstrainedBox一樣顯示溢出警告(Column亦同)

https://ithelp.ithome.com.tw/upload/images/20220924/20108931EcgaL6oBoi.png

解決方法:使用Exapanded
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Row(
        children: [
          Expanded(
            child: Container(
              color: Colors.red,
              child: const Text(
                'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!',
              ),
            ),
          ),
          Container(color: Colors.blue, child: const Text('Hello Test!')),
        ],
      )
    );
  }
}

也就是

Row(
	children: [
	  Expanded(
		child: Container(
		  color: Colors.red,
		  child: const Text(
			'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!',
		  ),
		),
	  ),
	  Container(color: Colors.blue, child: const Text('Hello Test!')),
	],
)

當Row的子widget被包在了Expanded widget後,Row 會根據所有 Expanded 的子級來計算其該有的寬度。 使用Expanded,子widget自身的寬度就變得無關緊要,直接會被忽略掉

https://ithelp.ithome.com.tw/upload/images/20220924/20108931u0eYSzkANI.png

解決方法:使用Flexible
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Row(
        children: [
          Flexible(
            child: Container(
              color: Colors.red,
              child: const Text(
                'Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!Example Text Test !!',
              ),
            ),
          ),
          Container(color: Colors.blue, child: const Text('Hello Test!')),
        ],
      )
    );
  }
}

使用Flexible而不是Expanded的唯一的區別是

  • Flexible 會讓其子級具有與 Flexible 相同或者更小的寬度
  • Expanded 將會強制其子級具有和 Expanded 相同的寬度

但無論是Expanded還是Flexible在它們決定子級大小時都會忽略其寬度。

https://ithelp.ithome.com.tw/upload/images/20220924/20108931Noejr2tEpa.png


今天簡單的介紹一些常見會出現的例子,我們明天見


上一篇
8.flutter的布局(一)
下一篇
10.flutter的布局(三)
系列文
30天學習flutter30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言