You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Flutter应用适配不同屏幕尺寸实现响应式布局?

如何开发适配不同屏幕尺寸的响应式Flutter应用

要打造适配各类屏幕的响应式Flutter应用,核心是避免硬编码固定数值,改用动态计算的相对单位。结合你提供的登录页面代码,以下是具体实现方案:

核心实现技巧

  • 基于MediaQuery获取屏幕参数:通过MediaQuery.of(context)获取屏幕宽高、像素密度等核心参数,所有布局尺寸都基于这些参数动态计算。
  • 替换固定值为相对单位:将固定的padding、间距、控件尺寸改为屏幕宽高的百分比,确保在手机、平板等不同设备上布局比例一致。
  • 动态适配字体大小:避免固定fontSize,结合屏幕宽度与系统textScaleFactor调整字体,兼顾显示效果与系统无障碍设置。
  • 保留SafeArea与SingleChildScrollView:确保内容不被状态栏、导航栏遮挡,且在小屏幕上可滚动查看完整内容。

修改后的响应式登录页面代码

@override
Widget build(BuildContext context) {
  final screenWidth = MediaQuery.of(context).size.width;
  final screenHeight = MediaQuery.of(context).size.height;
  final textScaleFactor = MediaQuery.of(context).textScaleFactor;

  return Scaffold(
    backgroundColor: Appcolors.white,
    appBar: AppBar(
      backgroundColor: Appcolors.white,
      elevation: 0,
    ),
    body: SingleChildScrollView(
      child: SafeArea(
        child: Column(
          children: [
            Padding(
              padding: EdgeInsets.fromLTRB(0, screenHeight * 0.04, 0, 0),
              child: Center(
                child: Text(
                  'Welcome Back!',
                  style: TextStyle(
                    fontSize: 28 * textScaleFactor,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ),
            ),
            SizedBox(height: screenHeight * 0.01),
            Text(
              'Please enter your account here',
              style: TextStyle(
                fontSize: 14 * textScaleFactor,
                fontWeight: FontWeight.w400,
              ),
            ),

            SizedBox(height: screenHeight * 0.08),
            // Email textfield
            Padding(
              padding: EdgeInsets.symmetric(horizontal: screenWidth * 0.08),
              child: TextField(
                cursorColor: Appcolors.black,
                decoration: InputDecoration(
                  fillColor: Appcolors.white,
                  filled: true,
                  prefixIcon: const Icon(Icons.email_outlined, color: Appcolors.black),
                  label: const Text('Email'),
                  enabledBorder: OutlineInputBorder(
                    borderSide: const BorderSide(color: Appcolors.textfieldborder),
                    borderRadius: BorderRadius.circular(32),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: const BorderSide(color: Appcolors.buttonColor),
                    borderRadius: BorderRadius.circular(32),
                  ),
                ),
              ),
            ),

            SizedBox(height: screenHeight * 0.04),
            // Password textfield
            Padding(
              padding: EdgeInsets.symmetric(horizontal: screenWidth * 0.08),
              child: TextField(
                cursorColor: Appcolors.black,
                decoration: InputDecoration(
                  prefixIcon: const Icon(Icons.lock, color: Appcolors.black),
                  label: const Text('Password'),
                  labelStyle: TextStyle(color: Appcolors.buttonColor),
                  suffixIcon: IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.remove_red_eye_outlined, color: Appcolors.black),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: const BorderSide(color: Appcolors.textfieldborder),
                    borderRadius: BorderRadius.circular(32),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: const BorderSide(color: Appcolors.buttonColor),
                    borderRadius: BorderRadius.circular(32),
                  ),
                ),
              ),
            ),

            Padding(
              padding: EdgeInsets.fromLTRB(screenWidth * 0.4, screenHeight * 0.01, 0, 0),
              child: TextButton(
                onPressed: () {},
                child: Text(
                  'Forgot password?',
                  style: TextStyle(
                    color: Appcolors.forgotpassword,
                    fontSize: 14 * textScaleFactor,
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ),
            ),

            SizedBox(height: screenHeight * 0.04),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                minimumSize: Size(screenWidth * 0.85, screenHeight * 0.075),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(32)),
                elevation: 0,
                primary: Appcolors.buttonColor,
              ),
              onPressed: () {},
              child: Text(
                'Login',
                style: TextStyle(
                  shadows: const [
                    Shadow(offset: Offset(5.0, 5.0), blurRadius: 12.0)
                  ],
                  fontSize: 16 * textScaleFactor,
                  fontWeight: FontWeight.w700,
                ),
              ),
            ),

            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  'Don\'t have any account?',
                  style: TextStyle(
                    fontSize: 14 * textScaleFactor,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                TextButton(
                  onPressed: () {},
                  child: Text(
                    'Sign Up',
                    style: TextStyle(
                      fontSize: 14 * textScaleFactor,
                      fontWeight: FontWeight.w700,
                      color: Appcolors.buttonColor,
                    ),
                  ),
                )
              ],
            ),

            SizedBox(height: screenHeight * 0.02),

            Row(
              children: [
                Expanded(
                  child: Divider(
                    thickness: 1.5,
                    indent: screenWidth * 0.08,
                    endIndent: screenWidth * 0.03,
                  ),
                ),
                Text(
                  'Sign in with',
                  style: TextStyle(fontSize: 14 * textScaleFactor),
                ),
                Expanded(
                  child: Divider(
                    thickness: 1.5,
                    indent: screenWidth * 0.03,
                    endIndent: screenWidth * 0.08,
                  ),
                )
              ],
            ),

            SizedBox(height: screenHeight * 0.05),

            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                InkWell(
                  onTap: () {},
                  child: Image(
                    image: const AssetImage('assets/images/Facebook_Logo.png'),
                    height: screenHeight * 0.04,
                  ),
                ),
                SizedBox(width: screenWidth * 0.12),
                InkWell(
                  onTap: () {},
                  child: Image(
                    image: const AssetImage('assets/images/Google__Logo.png'),
                    height: screenHeight * 0.04,
                  ),
                )
              ],
            )
          ],
        ),
      ),
    ),
  );
}

关键修改说明

  1. 定义screenWidth、screenHeight、textScaleFactor三个核心变量,所有布局数值都基于这些变量计算
  2. 将固定的padding、间距改为屏幕宽高的百分比,确保布局比例在不同设备上一致
  3. 字体大小乘以textScaleFactor,适配系统字体缩放设置,提升无障碍体验
  4. 登录按钮宽度设为屏幕宽度的85%,避免在小屏幕上超出可视范围
  5. 分割线的indent/endIndent改为屏幕宽度的百分比,保证左右间距比例一致

内容的提问来源于stack exchange,提问作者Martins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:15:33