Flutter中如何在多组件Row里实现自适应子组件的正方形Widget
问题描述
我需要在Row中放置多个组件,要求第一个组件为正方形,边长由子组件(此处为Text)的固有宽高中的较小值决定,基于子Text自动调整为正方形。给Text添加均等内边距的效果不佳,我曾尝试使用AspectRatio组件,虽能得到正方形,但它会尽可能放大而非占用最小空间。请问如何在不硬编码宽高的情况下实现该效果?
初始示例代码
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 100), Text(' 当前状态:'), SizedBox(height: 5), Row( children: [ Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.red.shade300, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.blue), ), child: const Center( child: Text( 'square', textAlign: TextAlign.center, ), ), ), SizedBox(width: 10), Flexible( child: Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue.shade200, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.red), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'placeholder for other widgets', style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), ], ), SizedBox(height: 50), Text(' 期望效果:'), SizedBox(height: 5), Row( children: [ SizedBox( height: 50, child: AspectRatio( aspectRatio: 1, child: Container( decoration: BoxDecoration( color: Colors.red.shade300, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.blue), ), child: Center( child: Text( 'square', textAlign: TextAlign.center, ), ), ), ), ), SizedBox(width: 10), Flexible( child: Container( height: 50, decoration: BoxDecoration( color: Colors.blue.shade200, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.red), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'placeholder for other widgets', style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), ], ), ], ), ), ); } }
尝试的解决方案
我更新了代码,尝试了两种实现方式:
方式1:StatefulWidget + GlobalKey获取尺寸
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final GlobalKey _key = GlobalKey(); double boxWidth = 0.0; final String inputText = 'square'; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { BuildContext? keyContext = _key.currentContext; if (keyContext != null) { RenderBox renderBox = keyContext.findRenderObject() as RenderBox; boxWidth = renderBox.size.width; } }); }); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 100), Text(' 输入文本: $inputText'), const SizedBox(height: 50), const Text(' 使用StatefulWidget实现'), const SizedBox(height: 10), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(10), key: _key, height: boxWidth, decoration: BoxDecoration( color: Colors.red.shade300, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.blue), ), child: Center( child: Text( inputText, textAlign: TextAlign.center, ), ), ), const SizedBox(width: 10), Flexible( child: Container( padding: const EdgeInsets.all(10), height: boxWidth, decoration: BoxDecoration( color: Colors.blue.shade200, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.red), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'placeholder for other widgets', style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), ], ), const SizedBox(height: 50), const Text(' 使用AspectRatio实现'), const SizedBox(height: 10), Row( children: [ Flexible( flex: 2, child: AspectRatio( aspectRatio: 1, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.red.shade300, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.blue), ), child: Center( child: Text( inputText, textAlign: TextAlign.center, ), ), ), ), ), const SizedBox(width: 10), Flexible( flex: 8, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue.shade200, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.red), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'placeholder for other widgets', style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), ], ), ], ), ), ); } }
效果对比

最优解决方案
可以通过IntrinsicWidth + AspectRatio的组合实现需求,无需硬编码尺寸或使用StatefulWidget,能自动基于子组件的最小固有尺寸生成正方形:
Row( children: [ IntrinsicWidth( child: AspectRatio( aspectRatio: 1, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.red.shade300, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.blue), ), child: const Center( child: Text( 'square', textAlign: TextAlign.center, ), ), ), ), ), const SizedBox(width: 10), Flexible( child: Container( padding: const EdgeInsets.all(10), alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blue.shade200, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.red), ), child: Text( 'placeholder for other widgets', style: Theme.of(context).textTheme.bodySmall, ), ), ), ], )
原理说明
IntrinsicWidth会先计算子组件的固有宽度(即Text所需的最小宽度),并将该宽度作为自身宽度。AspectRatio设置为1时,会将高度调整为与宽度一致,从而生成正方形。- 这种方式完全基于子组件的固有尺寸,不会像单独使用AspectRatio那样过度放大,代码简洁且可靠。
内容的提问来源于stack exchange,提问作者js182
相关产品推荐
相关产品推荐

