Reputation: 579
Let's say that i have a scrollable page and inside this page i have another scrollable listview(vertical), so i want when child listview reached end, the scrollable page start moving to it's end. Also when child listview reached top, scrollable page start moving to it's top. how can do that?
here's my codes
Widget FreshProductsShow(double pageHeight, double pageWidth) {
return Container(
height: pageHeight / 1.3,
width: pageWidth,
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return Card(
child: Container(
width: pageWidth,
// height: pageHeight / 7,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(10)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
textDirection: TextDirection.rtl,
children: [
Image.asset(
"images/peper.png",
width: pageWidth / 4,
height: pageHeight / 8,
),
Padding(
padding: EdgeInsets.only(left: pageWidth / 6.3),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(
left: pageWidth / 10, top: pageHeight / 45),
child: AutoSizeText(
"peper",
style: TextStyle(
fontSize: pageHeight / 48,
fontWeight: FontWeight.bold,
color: Color(0xff54595F)),
),
),
],
),
)
],
),
alignment: Alignment.centerRight,
),
elevation: 5,
);
},
scrollDirection: Axis.vertical,
),
);
}
Upvotes: 6
Views: 2677
Reputation: 1549
Yes, there is way to scroll parent widget if child widget is either on top or bottom by using Simulation.
Here is sample code to create custom physics:
import 'package:flutter/material.dart';
class CustomScrollPhysics extends ClampingScrollPhysics {
final Function outerController;
bool isMinCheck = false;
CustomScrollPhysics({required this.outerController, ScrollPhysics? parent})
: super(parent: parent);
@override
CustomScrollPhysics applyTo(ScrollPhysics? ancestor) {
return CustomScrollPhysics(
outerController: outerController,
parent: buildParent(ancestor)!,
);
}
@override
Simulation? createBallisticSimulation(
ScrollMetrics position,
double velocity,
) {
if (position.pixels >= position.maxScrollExtent && velocity >= 0.0) {
outerController(velocity, false);
} else if (position.pixels == position.minScrollExtent && isMinCheck) {
outerController(velocity, true);
} else {
isMinCheck = true;
}
return super.createBallisticSimulation(position, velocity);
}
}
Below is sample code to implement in parent widget -
import 'package:flutter/material.dart';
import 'package:lurnify/ui/widgets/custom_scroll.dart';
class CustomScrolling extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _CustomState();
}
}
class _CustomState extends State<CustomScrolling>
with TickerProviderStateMixin {
final outerPhysics = ClampingScrollPhysics();
ScrollController innerControllers = ScrollController();
ScrollController outerController = ScrollController();
void innerListener(double velocity, ScrollController outerController) {
final sim = outerPhysics.createBallisticSimulation(
outerController.position,
velocity,
);
if (sim != null) {
ScrollActivity _test = BallisticScrollActivity(
outerController.position.activity!.delegate,
sim,
this,
);
outerController.position.beginActivity(_test);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(), body: SafeArea(child: body()));
}
Widget body() {
return SingleChildScrollView(
controller: outerController,
physics: outerPhysics,
child: Column(
children: [
Container(width: double.infinity, height: 200, color: Colors.brown),
Container(
width: double.infinity,
height: 500,
color: Colors.red,
child: ListView.builder(
shrinkWrap: true,
itemCount: 40,
physics: CustomScroll(
outerController:
(velocity, isMin) =>
innerListener(velocity, outerController),
),
itemBuilder: (context, index) {
return Container(child: Text("index $index"));
},
),
),
Container(color: Colors.grey, width: double.infinity, height: 400),
],
),
);
}
}
Upvotes: 1
Reputation: 1270
To find the scroll position you have to use the Scroll Notification Listener NotificationListener
, and using a ScrollController
we can control the scroll position for each Scrollable Widget.
Here is your function converted to a StatefulWidget, and the logic implemented if I got it correctly:
class FreshProductsShow extends StatefulWidget {
const FreshProductsShow(
{Key? key, required this.pageHeight, required this.pageWidth})
: super(key: key);
final double? pageHeight;
final double? pageWidth;
@override
_FreshProductsShowState createState() => _FreshProductsShowState();
}
class _FreshProductsShowState extends State<FreshProductsShow> {
ScrollController _childScrollController = ScrollController();
ScrollController _parentScrollController = ScrollController();
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: _parentScrollController,
child: Column(
children: [
Container(
color: Colors.red,
height: 300,
),
Container(
height: widget.pageHeight! / 1.3,
width: widget.pageWidth,
child: NotificationListener(
onNotification: (ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.pixels ==
notification.metrics.maxScrollExtent) {
debugPrint('Reached the bottom');
_parentScrollController.animateTo(
_parentScrollController.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.easeIn);
} else if (notification.metrics.pixels ==
notification.metrics.minScrollExtent) {
debugPrint('Reached the top');
_parentScrollController.animateTo(
_parentScrollController.position.minScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.easeIn);
}
}
return true;
},
child: ListView.builder(
controller: _childScrollController,
itemCount: 10,
itemBuilder: (context, index) {
return Card(
child: Container(
width: widget.pageWidth,
// height: pageHeight / 7,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
textDirection: TextDirection.rtl,
children: [
Image.asset(
"images/peper.png",
width: widget.pageWidth! / 4,
height: widget.pageHeight! / 8,
),
Padding(
padding:
EdgeInsets.only(left: widget.pageWidth! / 6.3),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(
left: widget.pageWidth! / 10,
top: widget.pageHeight! / 45),
child: Text(
"peper",
style: TextStyle(
fontSize: widget.pageHeight! / 48,
fontWeight: FontWeight.bold,
color: Color(0xff54595F)),
),
),
],
),
)
],
),
alignment: Alignment.centerRight,
),
elevation: 5,
);
},
scrollDirection: Axis.vertical,
),
),
),
Container(
color: Colors.red,
height: 300,
),
],
),
);
}
}
Upvotes: 1