Flutter: Background Image Moves Up When Tapping on TextFormField – How to Prevent Layout Shift?
I have a CustomScaffold
widget that contains a background image, and the body of the screen is placed inside a Scaffold
widget. However, when I tap on a TextFormField
, the background image shifts upward along with the body content. I want to prevent the background image from shifting when the keyboard appears without breaking my layout.
What I Tried:
I set resizeToAvoidBottomInset: false
in my Scaffold
, which should have prevented the body from resizing when the keyboard appears, but the background image still shifts up.
I wrapped the scrollable elements with a SingleChildScrollView
to make it scrollable, but it didn’t fix the issue.
I found a solution by wrapping the background image in a SingleChildScrollView
. This allowed the background to remain fixed while the body could still scroll independently. Below is my updated CustomScaffold
widget that fixed the issue:
class CustomScaffold extends StatelessWidget {
final Widget body;
final Color? backgroundColor;
final PreferredSizeWidget? appBar;
final Widget? bottomNavigationBar;
final bool? extendBodyBehindAppBar;
final bool? resizeToAvoidBottomInset;
final bool bgImg;
final String productType;
const CustomScaffold({
Key? key,
required this.body,
this.backgroundColor,
this.appBar,
this.bottomNavigationBar,
this.extendBodyBehindAppBar = false,
this.resizeToAvoidBottomInset,
this.bgImg = false,
this.productType = "",
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
},
child: Stack(
children: [
bgImg == false && productType.isEmpty
? SizedBox.shrink()
: Positioned.fill(
left: 0,
top: 0,
right: 0,
bottom: 0,
child: SingleChildScrollView( // Added SingleChildScrollView
child: Image.asset(
_getBackgroundImage(productType),
fit: BoxFit.fill,
),
)),
Scaffold(
extendBodyBehindAppBar: extendBodyBehindAppBar ?? false,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
appBar: appBar,
backgroundColor: backgroundColor ??
(bgImg == false
? Constants.colors.background
: Colors.transparent),
body: body,
bottomNavigationBar: bottomNavigationBar,
),
],
),
);
}
String _getBackgroundImage(String? productType) {
switch (productType) {
case "A":
return 'assets/images/pl_bg.webp';
case "B":
return 'assets/images/pl_bg.webp';
case "C":
return 'assets/images/pl_bg.webp';
case "D":
return 'assets/images/pl_bg.webp';
case "E":
return 'assets/images/pl_bg.webp';
case "F":
return 'assets/images/pl_bg.webp';
case "G":
return 'assets/images/ce_bg.webp';
default:
return 'assets/images/splash.webp';
}
}
}
and here is my UI
child: CustomScaffold(
resizeToAvoidBottomInset: false,
bgImg : true,
productType: "A",
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LabelWidget(
text: 'My UI'.tr,
fontWeight: FontWeight.w600,
fontSize: 28.sp,
textColor: Colors.black,
padding: 0,
).marginSymmetric(horizontal: 16.w),
// Select the card for offers
CardWidgetForOffers(
offerController: controller,
entitiesController: entController), // select card
16.verticalSpace,
Expanded(
child: SingleChildScrollView(
controller: scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Banners widget swipe cards
BannerWidget(),
// saved offers
SavedOffersWidget(
offersController: controller),
// search offers here [This is my TextFormField]
SearchOffersWidget(offersController: offersController),
// offers in list view
NewTestListViewWidget(),
15.verticalSpace,
],
),
),
),
],
).marginOnly(top: 50.h),
),