Flutter中SingleChildScrollView与Stack、Container搭配异常求助
Flutter登录页背景图不显示且屏幕变黑问题解决
我在Flutter开发中,尝试在Scaffold内用Container设置背景图,再通过SingleChildScrollView嵌套Stack堆叠内容时,出现屏幕变黑且背景图无法显示的问题,问题出在login.dart文件的60-70行。以下是完整代码:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'authentication.dart'; class LoginScreen extends StatelessWidget { const LoginScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return WillPopScope(onWillPop: () async => false, child: Scaffold( backgroundColor: Colors.transparent, appBar: AppBar( elevation: 0, bottomOpacity: 0.0, title: IconButton(icon:Image.asset('assets/topImage.png'), iconSize: 65, onPressed: () { Navigator.pop(context); }, ), centerTitle: true, ), body: const Inputs(), extendBodyBehindAppBar: true, ) ); } } class Inputs extends StatefulWidget { const Inputs({Key? key}) : super(key: key); @override State<Inputs> createState() => _InputsState(); } class _InputsState extends State<Inputs> { final emailInController = TextEditingController(); final passwordInController = TextEditingController(); final emailUpController = TextEditingController(); final passwordUpController = TextEditingController(); String loginTitle = "Login"; bool _isVisible = true; String buttonText = "Sign In"; String infoText = "Enter your email above and click \"Reset Password\". An email will be sent to reset your password"; @override void dispose() { emailInController.dispose(); passwordInController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SingleChildScrollView( child: Stack(children: [ Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/bgImage.jpeg"), fit: BoxFit.cover, ), ), ), Column( children: <Widget>[ const SizedBox(height:120), // Top Login Text Center(child: Text(loginTitle, style: const TextStyle(fontSize: 35, fontFamily: 'samarkan')),), // Email Enter (Sign-In) Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: TextField( controller: emailInController, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Email', hintText: 'Enter valid email id as abcgamil.com' ) ), ), Visibility( visible: _isVisible, child: Column( children: [ // Sign In Password Padding( padding: const EdgeInsets.only( left: 15.0, right: 15.0, top: 15, bottom: 0 ), child: TextField( controller: passwordInController, obscureText: true, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Password', hintText: 'Enter Secure Password' ), ) ), // Forgot Password Text TextButton( onPressed: (){ setState(() { loginTitle = "Password Reset"; _isVisible = !_isVisible; buttonText = "Reset Password"; }); }, child: const Text('Forgot Password', style: TextStyle(color: Colors.black, fontSize: 15),), ), ], ) ), // Text Above Submit Visibility( visible: !_isVisible, child: Text(infoText, style: const TextStyle(fontSize: 20, fontFamily: "Georgia"), textAlign: TextAlign.center) ), // Sign In Submit InkWell( onTap: () { context.read<AuthenticationService>().signIn( email: emailInController.text.trim(), password: passwordInController.text.trim(),); passwordInController.text = ""; if(!_isVisible) { context.read<AuthenticationService>().passReset(email: emailInController.text); setState(() { _isVisible = true; buttonText = "Sign In"; loginTitle = "Login"; }); } emailInController.text = ""; }, child: Ink( height: 50, width: 250, decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(20) ), child: Center(child: Text(buttonText, style: const TextStyle(color: Colors.white))) ) ), // Sign Up Text Divider(height: 40, color: Colors.grey[800], indent: 10, endIndent: 10,), const Center(child: Text("Sign Up", style: TextStyle(fontSize: 35, fontFamily: 'samarkan'))), // Sign Up Email Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: TextField( controller: emailUpController, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Email', hintText: 'Enter valid email id as abcgamil.com' ) ), ), // Sign Up Password Padding( padding: const EdgeInsets.only( left: 15.0, right: 15.0, top: 15, bottom: 0 ), child: TextField( controller: passwordUpController, obscureText: true, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Password', hintText: 'Enter Secure Password' ), ) ), // Sign Up Submit Padding( padding: const EdgeInsets.only(top: 15), child: InkWell( onTap: () { context.read<AuthenticationService>().signUp( email: emailUpController.text.trim(), password: passwordUpController.text.trim(),); emailInController.text = ""; passwordInController.text = ""; }, child: Ink( height: 50, width: 250, decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(20) ), child: const Center(child: Text("Sign Up", style: TextStyle(color: Colors.white))) ) ) ), ] ), ]) ); } }
问题原因
作为背景的Container没有设置尺寸约束,SingleChildScrollView的子组件Stack在无约束时,Container会被压缩到最小尺寸,导致背景图无法显示;同时Stack上层的Column默认背景透明,Scaffold的backgroundColor设为transparent,最终显示的是Flutter默认的底层黑色画布。
修复方案
给背景Container添加尺寸约束,确保它铺满整个屏幕空间,以下是两种可行的修改方式:
方式一:使用MediaQuery设置固定宽高
修改Inputs组件build方法中的Stack部分:
@override Widget build(BuildContext context) { return SingleChildScrollView( child: Stack( children: [ SizedBox( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/bgImage.jpeg"), fit: BoxFit.cover, ), ), ), ), // 原有Column内容保持不变 Column( children: <Widget>[ // ... 原有代码 ... ] ), ] ) ); }
方式二:使用Positioned.fill自动填充Stack
这种方式更优雅,无需手动设置宽高:
@override Widget build(BuildContext context) { return SingleChildScrollView( child: ConstrainedBox( // 确保ScrollView子组件至少占满屏幕高度 constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height, ), child: Stack( children: [ Positioned.fill( child: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/bgImage.jpeg"), fit: BoxFit.cover, ), ), ), ), Column( children: <Widget>[ // ... 原有代码 ... ] ), ] ), ), ); }
额外注意事项
- 确认
assets/bgImage.jpeg已在pubspec.yaml中正确配置资源路径,示例配置:assets: - assets/bgImage.jpeg - assets/topImage.png - 如果内容较长,SingleChildScrollView依然可以正常滚动,不会影响背景图显示
内容的提问来源于stack exchange,提问作者PraneethO
相关产品推荐
相关产品推荐

