flutter-layout

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Goal

目标

Constructs robust, responsive Flutter user interfaces by composing layout widgets, managing constraints, and implementing adaptive design patterns. Assumes the target environment has the Flutter SDK installed and the user is familiar with Dart syntax and state management fundamentals.
通过组合布局组件、管理约束以及实现自适应设计模式,构建健壮、响应式的Flutter用户界面。本指南假设目标环境已安装Flutter SDK,且用户熟悉Dart语法和状态管理基础。

Instructions

使用说明

  1. Determine Layout Strategy (Decision Logic) Analyze the UI requirements and select the appropriate base layout widgets using the following decision tree:
    • Is the content strictly 1-Dimensional?
      • Horizontal arrangement -> Use
        Row
        .
      • Vertical arrangement -> Use
        Column
        .
    • Does the content need to overlap (Z-axis)?
      • Yes -> Use
        Stack
        with
        Positioned
        or
        Align
        children.
    • Does the content exceed the screen size?
      • Yes, 1D list -> Use
        ListView
        .
      • Yes, 2D grid -> Use
        GridView
        .
      • Yes, custom scroll effects -> Use
        CustomScrollView
        with Slivers.
    • Does the layout need to adapt to screen size changes?
      • Yes -> Use
        LayoutBuilder
        or
        MediaQuery
        .
  2. Apply the Golden Rule of Constraints Enforce Flutter's core layout rule: Constraints go down. Sizes go up. Parent sets position. When a widget requires a specific size, ensure its parent allows it. Use
    ConstrainedBox
    to inject specific constraints, but remember it only adds to the parent's constraints.
    dart
    // Example: Forcing a specific size within a flexible parent
    Center(
      child: ConstrainedBox(
        constraints: const BoxConstraints(
          minWidth: 70,
          minHeight: 70,
          maxWidth: 150,
          maxHeight: 150,
        ),
        child: Container(
          color: Colors.blue,
          width: 1000, // Will be constrained to 150 (max)
          height: 10,  // Will be constrained to 70 (min)
        ),
      ),
    )
  3. Implement Adaptive Layouts For screens that must support both mobile and tablet/desktop form factors, implement a
    LayoutBuilder
    to branch the UI logic based on available width.
    dart
    class AdaptiveScreen extends StatelessWidget {
      const AdaptiveScreen({super.key});
    
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: LayoutBuilder(
              builder: (context, constraints) {
                if (constraints.maxWidth > 600) {
                  return _buildWideLayout();
                } else {
                  return _buildNarrowLayout();
                }
              },
            ),
          ),
        );
      }
    
      Widget _buildWideLayout() {
        return Row(
          children: [
            const SizedBox(width: 250, child: Placeholder()), // Sidebar
            Container(width: 1, color: Colors.grey), // Divider
            const Expanded(child: Placeholder()), // Main Content
          ],
        );
      }
    
      Widget _buildNarrowLayout() {
        return const Column(
          children: [
            Expanded(child: Placeholder()), // Main Content
          ],
        );
      }
    }
  4. Compose Flex Layouts (Rows and Columns) When using
    Row
    or
    Column
    , manage child sizing using
    Expanded
    (forces child to fill available space) or
    Flexible
    (allows child to be smaller than available space).
    dart
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        const Icon(Icons.star),
        Expanded(
          flex: 2,
          child: Container(color: Colors.red, height: 50),
        ),
        Flexible(
          flex: 1,
          child: Container(color: Colors.blue, height: 50),
        ),
      ],
    )
  5. Gather Context STOP AND ASK THE USER: "What are the target devices (e.g., mobile, tablet, web) and specific breakpoint widths required for this layout?"
  6. Validate-and-Fix: Handle Unbounded Constraints Verify that no
    Expanded
    or
    Flexible
    widgets are placed inside unbounded parents (like
    ListView
    or
    SingleChildScrollView
    ). If a RenderFlex overflow occurs, implement the following fix:
    dart
    // INCORRECT: Expanded inside a scrollable view causes unbounded height errors.
    // SingleChildScrollView(child: Column(children: [Expanded(child: Container())]))
    
    // CORRECT: Use a bounded height or remove Expanded.
    // Alternatively, use CustomScrollView with SliverFillRemaining:
    CustomScrollView(
      slivers: [
        SliverToBoxAdapter(child: HeaderWidget()),
        SliverFillRemaining(
          hasScrollBody: false,
          child: ExpandedContentWidget(),
        ),
      ],
    )
  1. 确定布局策略(决策逻辑) 分析UI需求,结合以下决策树选择合适的基础布局组件:
    • 内容是否为严格的一维结构?
      • 横向排列 -> 使用
        Row
      • 纵向排列 -> 使用
        Column
    • 内容需要层叠展示(Z轴)吗?
      • 是 -> 使用
        Stack
        ,搭配
        Positioned
        Align
        子组件
    • 内容超出屏幕尺寸吗?
      • 是,一维列表 -> 使用
        ListView
      • 是,二维网格 -> 使用
        GridView
      • 是,自定义滚动效果 -> 使用搭配Slivers的
        CustomScrollView
    • 布局需要适配屏幕尺寸变化吗?
      • 是 -> 使用
        LayoutBuilder
        MediaQuery
  2. 遵守约束黄金法则 遵循Flutter核心布局规则:约束向下传递,尺寸向上反馈,父组件决定位置。 当组件需要特定尺寸时,确保其父组件允许该尺寸。可使用
    ConstrainedBox
    添加特定约束,但请注意它只能在父组件约束基础上追加限制。
    dart
    // Example: Forcing a specific size within a flexible parent
    Center(
      child: ConstrainedBox(
        constraints: const BoxConstraints(
          minWidth: 70,
          minHeight: 70,
          maxWidth: 150,
          maxHeight: 150,
        ),
        child: Container(
          color: Colors.blue,
          width: 1000, // Will be constrained to 150 (max)
          height: 10,  // Will be constrained to 70 (min)
        ),
      ),
    )
  3. 实现自适应布局 针对需要同时支持手机、平板/桌面设备的屏幕,通过
    LayoutBuilder
    根据可用宽度分支实现UI逻辑。
    dart
    class AdaptiveScreen extends StatelessWidget {
      const AdaptiveScreen({super.key});
    
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: LayoutBuilder(
              builder: (context, constraints) {
                if (constraints.maxWidth > 600) {
                  return _buildWideLayout();
                } else {
                  return _buildNarrowLayout();
                }
              },
            ),
          ),
        );
      }
    
      Widget _buildWideLayout() {
        return Row(
          children: [
            const SizedBox(width: 250, child: Placeholder()), // Sidebar
            Container(width: 1, color: Colors.grey), // Divider
            const Expanded(child: Placeholder()), // Main Content
          ],
        );
      }
    
      Widget _buildNarrowLayout() {
        return const Column(
          children: [
            Expanded(child: Placeholder()), // Main Content
          ],
        );
      }
    }
  4. 组合弹性布局(Rows和Columns) 使用
    Row
    Column
    时,通过
    Expanded
    (强制子组件占满可用空间)或
    Flexible
    (允许子组件尺寸小于可用空间)管理子组件大小。
    dart
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        const Icon(Icons.star),
        Expanded(
          flex: 2,
          child: Container(color: Colors.red, height: 50),
        ),
        Flexible(
          flex: 1,
          child: Container(color: Colors.blue, height: 50),
        ),
      ],
    )
  5. 收集上下文信息 请暂停并询问用户: "本次布局的目标设备是哪些(例如手机、平板、网页)?需要的具体断点宽度是多少?"
  6. 验证与修复:处理无界约束问题 确认没有
    Expanded
    Flexible
    组件被放置在无界父容器内(比如
    ListView
    SingleChildScrollView
    )。如果出现RenderFlex溢出问题,可通过以下方案修复:
    dart
    // INCORRECT: Expanded inside a scrollable view causes unbounded height errors.
    // SingleChildScrollView(child: Column(children: [Expanded(child: Container())]))
    
    // CORRECT: Use a bounded height or remove Expanded.
    // Alternatively, use CustomScrollView with SliverFillRemaining:
    CustomScrollView(
      slivers: [
        SliverToBoxAdapter(child: HeaderWidget()),
        SliverFillRemaining(
          hasScrollBody: false,
          child: ExpandedContentWidget(),
        ),
      ],
    )

Constraints

约束条件

  • Always wrap top-level screen content in a
    SafeArea
    to prevent overlap with system UI.
  • Never place an
    Expanded
    or
    Flexible
    widget inside a parent that provides unbounded constraints (e.g.,
    SingleChildScrollView
    ,
    ListView
    , or
    Row
    inside a horizontally scrolling view).
  • Do not use
    Container
    solely for padding; use the
    Padding
    widget for better performance.
  • Assume the user is using Material 3 (
    useMaterial3: true
    is default).
  • 始终将顶层屏幕内容包裹在
    SafeArea
    内,避免与系统UI重叠。
  • 永远不要将
    Expanded
    Flexible
    组件放在提供无界约束的父组件内(例如
    SingleChildScrollView
    ListView
    ,或者横向滚动视图内的
    Row
    )。
  • 不要仅为了添加内边距就使用
    Container
    ;使用
    Padding
    组件可获得更好的性能。
  • 假设用户使用Material 3(默认配置为
    useMaterial3: true
    )。