How to make ListView preserve its scroll when transitioning to another route?
the way i created a "slide-out" animation in the question wasn't correct. in terms of the framework it's called a secondaryAnimation
to create your own secondary animation, you need to use a PageRouteBuilder
transitionBuilder property
example can the code below, which produces such animation, and there's no problem with a ListView
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primaryColor: Colors.white),
initialRoute: '/',
onGenerateInitialRoutes: (initialRoute) => [createCustomTransition(HomeScreen())],
onGenerateRoute: (settings) {
if (settings.name == '1') {
return createCustomTransition(SomeScreen());
}
return createCustomTransition(OtherScreen());
},
debugShowCheckedModeBanner: false,
);
}
}
/// Will create a custom route transition for you.
PageRouteBuilder createCustomTransition(Widget screen) {
return PageRouteBuilder(
transitionDuration: const Duration(milliseconds: 700),
reverseTransitionDuration: const Duration(milliseconds: 700),
pageBuilder: (context, animation, secondaryAnimation) => screen,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final slideAnimation = Tween(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
parent: animation,
));
final slideOutAnimation = Tween(
begin: Offset.zero,
end: const Offset(-0.3, 0.0),
).animate(CurvedAnimation(
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
parent: secondaryAnimation,
));
return SlideTransition(
position: slideAnimation,
child: SlideTransition(
position: slideOutAnimation,
child: child,
),
);
},
);
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key key}) : super(key: key);
final List<int> list = List.generate(1000, (index) => index);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) => ListTile(
title: Center(
child: Text(list[index].toString()),
)
),
)
),
ElevatedButton(
child: const Text('go to some screen'),
onPressed: () {
Navigator.of(context).pushNamed('1');
},
),
],
),
),
);
}
}
class SomeScreen extends StatelessWidget {
const SomeScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(),
body: Center(
child: ElevatedButton(
child: const Text('go to other screen'),
onPressed: () {
Navigator.of(context).pushNamed('');
},
),
),
);
}
}
class OtherScreen extends StatelessWidget {
const OtherScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(),
);
}
}
according to docs PageRouteBuilder
is
A utility class for defining one-off page routes in terms of callbacks.
it's perfrect for general use, but if you are building something more complex, i suggest taking a look at some framework page route animation impementations and different class relationships they have
- widgets/page_transitions_theme.dart - various page route builders
- material/route - android routes implementation
- cupertino/route - cupertion routes implementation
nt4f04und
Updated on December 14, 2022Comments
-
nt4f04und over 1 year
I want to accomplish slide to right transition in my flutter application. The problem is that route transition kinda creates new instance of page that I want to transit from, and so
ListView
scroll resets.That's how I create a new route
/// @oldRoute needed cause this route transition utilizes `SlideStackRightRoute` Route createSettingsRoute(Widget oldRoute) { return SlideStackRightRoute(exitPage: oldRoute, enterPage: SettingsRoute()); }
And finally slide transition class itself
import 'package:flutter/material.dart'; /// Creates cupertino-like route transition, where new route pushes old from right to left class SlideStackRightRoute extends PageRouteBuilder { final Widget enterPage; final Widget exitPage; static var exBegin = Offset(0.0, 0.0); static var exEnd = Offset(-0.5, 0.0); static var entBegin = Offset(1.0, 0.0); static var entEnd = Offset.zero; static var curveIn = Curves.easeOutSine; static var curveOut = Curves.easeInSine; SlideStackRightRoute({@required this.exitPage, @required this.enterPage}) : super( transitionDuration: Duration(milliseconds: 400), pageBuilder: ( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, ) => enterPage, transitionsBuilder: ( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child, ) => Stack( children: <Widget>[ SlideTransition( position: Tween(begin: exBegin, end: exEnd) .chain(CurveTween(curve: curveIn)) .chain(CurveTween(curve: curveOut)) .animate(animation), child: Container( foregroundDecoration: BoxDecoration( color: Colors.black.withOpacity(animation.value / 2), ), child: exitPage), ), SlideTransition( position: Tween(begin: entBegin, end: entEnd) .chain(CurveTween(curve: curveIn)) .chain(CurveTween(curve: curveOut)) .animate(animation), child: enterPage, ) ], ), ); }