Flutter —布局系统概述

移动开发 作者: 2024-08-24 21:10:01
老孟导读:此篇文章非常详细的讲解了 Flutter 布局系统的工作原理。 翻译自:https://itnext.io/flutter-layout-system-overview-c70bbe9ba9
  • 我的小部件的尺寸看起来不合适,怎么回事?
  • 我只想将Widget放置在特定位置,但是没有任何属性可以控制它,为什么呢?
  • 我一直看到诸如BoxConstraints,RenderBox和Size之类的术语。它们之间有什么关系?
  • 对布局系统如何工作有一个大概的了解?

“两个阶段” 布局系统和约束

Example

  • If *max(w|h) = min (w|h)*,that is *tightly* constrained.
  • If *min(w|h) = 0*,we have a *loose* constraint.
  • If *max(w|h) != infinite*,the constraint is *bounded.*
  • If *max(w|h) = infinite*,the constraint is *unbounded.*
  • If *min(w|h) = infinite*,is just said to be *infinite*
import 'package:flutter/material.dart';

GlobalKey _keyMyApp = GlobalKey();
GlobalKey _keyMaterialApp = GlobalKey();
GlobalKey _keyHomePage = GlobalKey();
GlobalKey _keyScaffold = GlobalKey();
GlobalKey _keyAppbar = GlobalKey();
GlobalKey _keyCenter = GlobalKey();
GlobalKey _keyFAB = GlobalKey();
GlobalKey _keyText = GlobalKey();

void printConstraint(String name,BoxConstraints c) {
  print(
    'CONSTRAINT of $name: min(w=${c.minWidth.toInt()},h=${c.minHeight.toInt()}) max(w=${c.maxWidth.toInt()},h=${c.maxHeight.toInt()})',);
}

void printSizes() {
  printSize('MyApp',_keyMyApp);
  printSize('MaterialApp',_keyMaterialApp);
  printSize('HomePage',_keyHomePage);
  printSize('Scaffold',_keyScaffold);
  printSize('Appbar',_keyAppbar);
  printSize('Center',_keyCenter);
  printSize('Text',_keyText);
  printSize('FAB',_keyFAB);
}

void printSize(String name,GlobalKey key) {
  final RenderBox renderBox = key.currentContext.findRenderObject();
  final size = renderBox.size;
  print("SIZE of $name: w=${size.width.toInt()},h=${size.height.toInt()}");
}

void printPositions() {
  printPosition('MyApp',_keyMyApp);
  printPosition('MaterialApp',_keyMaterialApp);
  printPosition('HomePage',_keyHomePage);
  printPosition('Scaffold',_keyScaffold);
  printPosition('Appbar',_keyAppbar);
  printPosition('Center',_keyCenter);
  printPosition('Text',_keyText);
  printPosition('FAB',_keyFAB);
}

void printPosition(String name,GlobalKey key) {
  final RenderBox renderBox = key.currentContext.findRenderObject();
  final position = renderBox.localToGlobal(Offset.zero);
  print("POSITION of $name: $position ");
}

void main() {
  runApp(LayoutBuilder(
    builder: (context,constraints) {
      printConstraint('MyApp',constraints);
      return MyApp();
    },));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      key: _keyMyApp,builder: (context,constraints) {
        printConstraint('MaterialApp',constraints);
        return MaterialApp(
          key: _keyMaterialApp,title: 'Flutter Demo',theme: ThemeData(
            primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,),home: LayoutBuilder(
            builder: (context,constraints) {
              printConstraint('HomePage',constraints);
              return HomePage(
                key: _keyHomePage,title: 'Flutter Demo Home Page',);
            },);
      },);
  }
}

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

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
    super.initState();
  }

  void _afterLayout(_) {
    printSizes();
    printPositions();
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context,constraints) {
      printConstraint('Scaffold',constraints);
      return Scaffold(
        backgroundColor: Colors.purple,key: _keyScaffold,appBar: AppBar(
          key: _keyAppbar,title: Text(widget.title),body: LayoutBuilder(
          builder: (context,constraints) {
            printConstraint('Center',constraints);
            return Center(
              key: _keyCenter,child: LayoutBuilder(builder: (context,constraints) {
                printConstraint('Text',constraints);
                return Text(
                  'You have pushed the button this many times:',key: _keyText,style: TextStyle(color: Colors.white),);
              }),);
          },floatingActionButton: LayoutBuilder(
          builder: (context,constraints) {
            printConstraint('FAB',constraints);
            return FloatingActionButton(
              key: _keyFAB,onPressed: printSizes,tooltip: 'Increment',child: Icon(Icons.add),);
    });
  }
}
  1. Scaffold告知Center其约束,让其选择在 0 < width < 392 和 0 < height < 697 中选择。请注意,最大高度为759(屏幕最大高度)减去80(AppBar选择的高度)。
  2. Center转到其子组件“Text”,转发相同的约束。
  3. Text选择一个足以显示其数据的大小(279:16),然后回复Center。
  4. 借助手上的几何信息(大小),Center可以在其笛卡尔系统内正确定位文本。作为父母,Center有权选择其子组件位置,在这种情况下,它决定将其居中。
  1. 然后,Center为自己选择一个大小,而不是仅选择一个“足够”的大小(如“Text”一样),而是决定尽可能大,因此受到了限制。
  2. Scaffold收到Center所需的尺寸,并且流程继续向其最后一个孩子:FAB
  3. FAB收到约束,然后将其首选大小返回给Scaffold(56:56)
  4. 最后,Scaffold还具有将每个孩子都放置在其笛卡尔系统内所需的所有几何信息。
  1. Size信息继续沿渲染树传播。
  2. 每个小部件都使用此信息将每个孩子放置在笛卡尔系统内。
  3. Scaffold回复HomePage,HomePage回复MaterialApp,MaterialApp回复MyApp。直到最后再次到达Main。
  4. Main获取此“最终”窗口小部件,并将其最终绑定到屏幕中。

有趣的事情要记住

  • 小部件不知道其在屏幕上的位置;它的父组件才知道。
  • 小部件可以选择想要的大小,但必须根据其父级的限制。
  • 约束向下传播,而大小向上传播。
  • 尝试了解约束条件,它们可能在以后有用。
原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_68026.html