This tutorial demonstrates how to implement a button that consistently remains at the bottom of the screen in a Flutter application, regardless of content scrolling. We’ll use the bottomSheet
property of the Scaffold
widget and enhance the user experience with animations and styling.
Overview
In this demo, we’ll create a screen with the following features:
- A scrollable list of paragraphs
- A persistent bottom button that stays in place while scrolling
- An animation for the button to slide up when the screen loads
- Styling to make the button visually appealing
Implementation
import 'package:flutter/material.dart'; class NormalPage extends StatefulWidget { const NormalPage({Key? key}) : super(key: key); @override State<NormalPage> createState() => _NormalPageState(); } class _NormalPageState extends State<NormalPage> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Sticky Bottom Button Demo"), backgroundColor: Colors.teal, elevation: 0, leading: IconButton( icon: const Icon(Icons.menu), onPressed: () {}, ), actions: <Widget>[ IconButton( icon: const Icon(Icons.comment), onPressed: () {}, ), IconButton( icon: const Icon(Icons.settings), onPressed: () {}, ), ], ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: List.generate( 20, (index) => Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Text( 'This is paragraph ${index + 1}', style: Theme.of(context).textTheme.bodyLarge, ), ), ), ), ), ), ), ), bottomSheet: SlideTransition( position: Tween<Offset>( begin: const Offset(0, 1), end: Offset.zero, ).animate(_animation), child: Container( width: double.infinity, padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.teal, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 5, offset: const Offset(0, -3), ), ], ), child: ElevatedButton( onPressed: () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Button pressed!')), ); }, style: ElevatedButton.styleFrom( primary: Colors.white, onPrimary: Colors.teal, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('Sticky Bottom Button', style: TextStyle(fontSize: 18)), ), ), ), ); } }