I have two pages, and I want to be able to transition between the two by dragging the first down into the second. The second page then has a DraggableScrollableSheet at the bottom that I want to be able to drag back up to turn back into the first page. I'm very new to this, so I've asked ChatGPT to help me out, and while it's given me a decent solution for the first bit, it can't animate the dragging on the sheet as a page transition. The code it gave me is below. Is this even possible? If yes, how could I go about it? Thanks!
import 'package:flutter/material.dart';
class DragTransitionView extends StatefulWidget {
@override
State<DragTransitionView> createState() => _DragTransitionViewState();
}
class _DragTransitionViewState extends State<DragTransitionView>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onFirstPageDragUpdate(DragUpdateDetails details) {
// Dragging down increases the controller value
if (details.primaryDelta! > 0) {
_controller.value +=
details.primaryDelta! / MediaQuery.of(context).size.height;
}
}
void _onFirstPageDragEnd(DragEndDetails details) {
if (_controller.value > 0.5) {
_controller.forward(); // Complete transition to the second page
} else {
_controller.reverse(); // Return to the first page
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Second Page
FadeTransition(
opacity: _controller,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.translate(
offset: Offset(
0,
MediaQuery.of(context).size.height *
(1 - _controller.value)),
child: child,
);
},
child: Container(
color: Colors.blue,
child: Stack(
children: [
Center(
child: Text(
'Second Page',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
// DraggableScrollableSheet
DraggableScrollableSheet(
initialChildSize: 0.3, // Initial height of the sheet
minChildSize: 0.1, // Minimum height
maxChildSize: 1.0, // Maximum height
builder: (BuildContext context,
ScrollController scrollController) {
return FadeTransition(
opacity: _controller,
child: Container(
color: Colors.white,
child: SingleChildScrollView(
controller: scrollController,
child: Center(
child: Text(
'Drag Up from Here',
style: TextStyle(
color: Colors.black, fontSize: 18),
),
),
),
),
);
},
),
],
),
),
),
),
// First Page
GestureDetector(
onVerticalDragUpdate: _onFirstPageDragUpdate,
onVerticalDragEnd: _onFirstPageDragEnd,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.translate(
offset: Offset(0,
MediaQuery.of(context).size.height * _controller.value),
child: Opacity(
opacity: 1 - _controller.value,
child: child,
),
);
},
child: Container(
color: Colors.red,
child: Center(
child: Text(
'First Page',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
),
),
],
),
);
}
}
I ended up not using Navigator 1.0, as it was taking too long. But I did something with the DraggableScrollable that you wanted to implement. This might be a long code.
import 'package:flutter/material.dart';
import 'package:page_transition/expand_button.dart';
class PageWidget extends StatefulWidget {
const PageWidget({super.key});
@override
State<PageWidget> createState() => _PageWidgetState();
}
class _PageWidgetState extends State<PageWidget> {
late final DraggableScrollableController dragController;
@override
void initState() {
super.initState();
dragController = DraggableScrollableController();
}
@override
void dispose() {
super.dispose();
dragController.dispose();
}
void goToNextPage() {
dragController.animateTo(0.9,
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
//Page 2 Contents
PageTwoContents(
onTap: goToNextPage,
),
//Lyrics
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
// width: 400, // if you remove this it will fill the width
child: DraggableScrollableSheet(
controller: dragController,
minChildSize: 0.1,
maxChildSize: 0.9,
initialChildSize: 0.9,
builder:
(BuildContext context, ScrollController scrollController) {
//So some new layout can be built when constraints changes.
//lowkey just using it to setstate and switch shrink to expand.
return LayoutBuilder(
builder: (context, constraints) {
return Container(
padding: const EdgeInsets.symmetric(
vertical: 14.0, horizontal: 12.0),
decoration: const BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6.0),
topRight: Radius.circular(6.0),
),
),
child: SingleChildScrollView(
controller: scrollController,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
//Controls
SizedBox(
height: 100,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Padding(
padding:
EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'This is Page 1',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14.0,
),
),
),
IconButton(
child:Text( !dragController.isAttached
? 'EXPAND'
: dragController.size > 0.1
? 'SHRINK'
: 'EXPAND',),
onTap: () {
if (dragController.size < 0.3) {
dragController.animateTo(0.4,
duration: const Duration(
milliseconds: 300),
curve: Curves.easeInOut);
} else {
dragController.animateTo(0.0,
duration: const Duration(
milliseconds: 300),
curve: Curves.easeInOut);
}
},
),
],
),
),
//Page 2 Contents
const PageOneContents()
],
),
),
);
},
);
},
),
),
),
],
),
);
}
}
I placed the two widgets on one page, but using the draggable scrollable to transition between them, just like you wanted. I also placed PageOneContents and PageTwoContents in the exact locations that will give you the customization you seek. There is also an expand button on the two widgets to show you how you could control the transitions. Also Scrolling down page 1 will reveal Page 2. if you're curious about PageOneContent and PageTwoContent here they are.
import 'package:flutter/material.dart';
class PageTwoContents extends StatelessWidget {
final VoidCallback? onTap;
const PageTwoContents({super.key, this.onTap});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
gradient: const LinearGradient(
colors: [
Color(0xEEE5EFFE),
Color(0xEEF5EEF0),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'This is Page Two',
style: TextStyle(
color: Colors.black,
),
),
ElevatedButton(onPressed: onTap, child: const Text('Bring up Page 1'))
],
)),
);
}
}
class PageOneContents extends StatelessWidget {
const PageOneContents({super.key});
@override
Widget build(BuildContext context) {
return const Align(
alignment: Alignment.topCenter,
child: SizedBox(
child: Text(
'The Sweet Chariot that takes you to valhalla is at the end of everything glorious\n\n'
'May your focus ever remain as sharp as an Axe drenched in the sweat of blacksmiths from Kattegatt\n\n'
'May your horse ride Bold and True\n\n'
'May your eyes stay sharp\n\n'
'May Odin welcome you with Joy for you have been Victorious\n\n',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14.0,
),
),
),
);
}
}
Here is a gif of how it looks