I'm getting ScrollController attached to multiple scroll views - flutter

I have a pageview inside it a custom widget in which I'm passing the scrollController after initialising. And also have some function to manipulate scrollController from that widget but whenever it reaches to run manipulation part it gives me this error.
Unhandled Exception: 'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 109 pos 12: '_positions.length == 1': ScrollController attached to multiple scroll views.
code
class WeekView<T> extends StatefulWidget {
#override
WeekViewState<T> createState() => WeekViewState<T>();
}
class WeekViewState<T> extends State<WeekView<T>> {
late ScrollController _scrollController;
late PageController _pageController;
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: _currentIndex);
_scrollController = ScrollController();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (widget.enableScrollToEvent) {
if (widget.scrollToEvent == ScrollToEvent.currentTime &&
_controller.events.last.endTime != null) {
_controller.addListener(() {
scrollToCurrentTime(_controller.events.last.endTime!);
});
} else {
_controller.addListener(scrollToEvent);
}
}
}
#override
Widget build(BuildContext context) {
return PageView.builder(
itemCount: _totalWeeks,
controller: _pageController,
onPageChanged: _onPageChange,
itemBuilder: (_, index) { return InternalWeekViewPage<T>(
scrollController: _scrollController,
);
},
),
}
the function,
void scrollToEvent() {
if (_pageController.hasClients) {
_pageController
.animateToPage(
_pageController.initialPage +
((_controller.events.last.date.getDayDifference(DateTime.now())) /
7)
.floor(),
curve: widget.pageTransitionCurve,
duration: widget.pageTransitionDuration,
)
.then((value) {
if (_scrollController.hasClients) {//<<<<<<<<<<< scrollController
if (_controller.events.last.endTime != null) {
_scrollController.animateTo(
math.max(
_controller.events.last.endTime!.hour * _hourHeight -
_scrollController.position.viewportDimension +
_hourHeight,
0),
duration: widget.scrollTransitionDuration,
curve: widget.scrollToEventCurve,
);
}
}
});
}
}
the custom widget,
class InternalWeekViewPage<T> extends StatelessWidget {
const InternalWeekViewPage({
required this.scrollController,
});
#override
Widget build(BuildContext context) {
return Expanded(
child: SingleChildScrollView(
controller: scrollController,
child:(...)
),);
}
}
Note-: I have removed some unnecessary part for more readability
when I scroll manually and then I do the manipulation then it doesn't give me any error but doing it directly give me error.
so why I'm getting this error even though I'm using it only in one place can anyone help me

From this code
itemBuilder: (_, index) {
return InternalWeekViewPage<T>(
scrollController: _scrollController,
);
Item builder returning List of InternalWeekViewPage and having the same _scrollController.
Instead of passing _scrollController, make InternalWeekViewPage statefullWidgte and create and initialize on initState.
Does it solve your issue?

Related

UPDATE PageController with PhotoViewGallery.builder

I'm creating an app gallery. and can not update the images, I am always coming across the first image although the pagecontroller is correct, please help me to solve this bug.
class _ViewerPageState extends State<ViewerPage> {
List<String> img = [];
int idx = 0;
late PageController pageController;
#override
void initState() {
super.initState();
pageController = PageController(initialPage: idx);
img = widget.media.map((e) => e.id).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: widget.medium.mediumType == MediumType.image
? PhotoViewGallery.builder(
pageController: pageController,
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
heroAttributes: PhotoViewHeroAttributes(
tag: Text(widget.medium.filename.toString())),
imageProvider: PhotoProvider(mediumId: img[index]));
},
itemCount: img.length,
onPageChanged: onPageChanged,
)
: VideoProvider(
mediumId: widget.medium.id,
),
),
);
}
void onPageChanged(int index) {
setState(() {
idx = index;
});
}
}
I also have this error when I press the return button after displaying the photos
'' Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method. ''

Flutter scroll slowly to the bottom of a dynamic Listview

I have a list view that its item may vary each time. I want to make auto scroll for it.
I tried this code
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(seconds: 10),
curve: Curves.easeOut);
It works perfectly for small list view but when the list view items are 100 or more it start moving so fast.
I also tried to make the duration longer when the list view have more items but it mess up
The issue was caused from the animation itself not the duration.
I solved it by increasing the duration and setting
curve: Curves.linear.
// Declaring the controller and the item size
ScrollController _scrollController;
final itemSize = 100.0;
// Initializing
#override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
super.initState();
}
// Your list widget (must not be nested lists)
ListView.builder(
controller: _scrollController,
itemCount: <Your list length>,
itemExtent: itemSize,
itemBuilder: (context, index) {
return ListTile(<your items>);
},
),
// With the listener and the itemSize, you can calculate which item
// is on screen using the provided callBack. Something like this:
void _scrollListener() {
setState(() {
var index = (_scrollController.offset / itemSize).round() + 1;
});
}
import 'dart:async';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _controller = ScrollController();
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Timer(
Duration(seconds: 2),
() => _controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
),
);
return Scaffold(
body: ListView.builder(itemBuilder:(context, index) {
return Text('hi');
},
controller: _controller,
)
);
}
}

Flutter/Dart - Get index number of PageView.Builder for use in a Text Widget?

I've got a PageView.builder within a StatelessWidget. I need to get the current index number of the currently viewed page to appear in a text widget in my build.
Was hoping I could simply use currentIndex.toString() as a variable in the text widget but Android Studio underlines it in red and warns me of undefined name currentIndex. How can I get the correct variable?
class StageBuilder extends StatelessWidget {
final List<SpeakContent> speakcrafts;
StageBuilder(this.speakcrafts);
final PageController controller = PageController(initialPage: 0);
#override
Widget build(context) {
return PageView.builder(
controller: controller,
itemCount: speakcrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(speakcrafts[currentIndex], context);
},
);
}
Widget createViewItem(SpeakContent speakcraft, BuildContext context) {
return Container(
child: Text(currentIndex.toString()),
)
}
}
You need to pass the currentIndex into your createViewItem
class StageBuilder extends StatelessWidget {
final List<SpeakContent> speakcrafts;
StageBuilder(this.speakcrafts);
final PageController controller = PageController(initialPage: 0);
#override
Widget build(context) {
return PageView.builder(
controller: controller,
itemCount: speakcrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(speakcrafts[currentIndex], context, currentIndex);
},
);
}
Widget createViewItem(SpeakContent speakcraft, BuildContext context, int currentIndex) {
return Container(
child: Text(currentIndex.toString()),
);
}
}

Flutter PageView keep rebuilding the main widget on setState value

currently flutter app structure
StackedHome has a pageview with 2 children
Pageview(parent):
HomeScreen(child#1)
Vertical PageView
bottom navigation bar
UserProfilePage(child#2)
HomeScreen should pass the index value to UserProfilePage, so when scrolling horizontally, we will get user profilescreen with id passed to that received from HomeScreen. based on the id passed i will display related user profile
Here is sample video showing the problem :
https://drive.google.com/file/d/1tIypNOHewcFSo2Pf-F97hsQGfDgNVqfW/view?usp=sharing
Problem:
i managed to do that and its working fine, but my problem on setState of that variable
setState(() {
_postIndex = postIndex;
});
on each HomeScreen > onPageChanged call i am updating the index value pass it to the parent (StackedHome) class, and since there is a setState to update profile index (UserProfilePage)...the whole app will be rebuild on each pageview change...
What i need is to disable that main widget to be rebuilt again and again on value update..
StackedHome
class StackedHome extends StatefulWidget {
final int data;
final Function(int) onDataChange;
const StackedHome({
this.data,
this.onDataChange,
Key key,
}) : super(key: key);
#override
_StackedHomeState createState() => _StackedHomeState();
}
class _StackedHomeState extends State<StackedHome>{
PageController pageController;
int _count = 0;
int _postIndex = 0;
void _postId(int postIndex) {
//This cuasing main screen to be rebuilt everytime on pageview scroll
//but getting the value correctly
setState(() {
_postIndex = postIndex;
});
}
#override
void initState() {
super.initState();
pageController = PageController();
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
int index = 0;
#override
Future<void> _refreshPosts() async {
PostApi postApi = PostApi();
setState(() {
postApi.fetchAllPosts();
});
}
Widget build(BuildContext context) {
PostApi postApi = PostApi();
return FutureBuilder(
future: postApi.fetchAllPosts(),
builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return apiError('No Connection Made');
break;
case ConnectionState.waiting:
case ConnectionState.active:
return ApiLoading(color:0xff000000);
break;
case ConnectionState.done:
if (snapshot.hasError) {
return apiError(snapshot.error.toString());
}
if (snapshot.hasData) {
return _drawPostsList(snapshot.data, context);
}
break;
}
return Container();
},
);
}
Widget _drawPostsList(List<Post> posts, BuildContext context) {
return PageView(
reverse: true,
children: <Widget>[
HomeScreen(
posts: posts,
index: index,
postId: _postId,//function Passed
),
UserProfilePage(
posts: posts,
index: _postIndex,
)
],
);
}
}
HomeScreen
class HomeScreen extends StatefulWidget {
#override
final List posts;
final int index;
final Function(int) postId;
int getPage() {
return value;
}
void setPage(int page) {
value = page;
}
HomeScreen({Key key, this.posts, this.index, this.postId}) : super(key: key);
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
final PageController _controller = PageController();
PageController _pageController = PageController();
int index = 0;
#override
void initState() {
super.initState();
//Set pageview inital page
_pageController = PageController(
keepPage: true,
initialPage: widget.getPage(),
);
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refreshPosts,
child: Stack(children: <Widget>[
PageView.builder(
controller: _pageController,
onPageChanged: (index) => setState(() {
.
widget.postId(index);//I am calling parent class and updating the vlaue with new index value
.
}),
scrollDirection: Axis.vertical,
itemBuilder: (context, position) {
//Build image lists
return _homeList(widget.posts, position);
},
),
BottomNavigation("light"),
]),
);
}
}
i hope my problem is clear enough....i need to pass the value to parent so i can pass it to second child which is the profile screen so it will show user profile realted to that post
Ohh wow, managed to solve this problem using provider and consumer, by listening to any update on index id... this post helped me to solve it https://medium.com/flutter-nyc/a-closer-look-at-the-provider-package-993922d3a5a5

Flutter:OnTap not working when Listview is scrolling

I tried to create a marquee Widget, I using the Listview implement auto-scrolling, but if I added a click event to the text when listview is scrolling, it not clickable.I know it should be a rolling problem, but I don't know how to solve itHere's the code is Marquee widget.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class MarqueeContinuous extends StatefulWidget {
final Widget child;
final Duration duration;
final double stepOffset;
MarqueeContinuous(
{Key key,
this.child,
this.duration = const Duration(seconds: 3),
this.stepOffset = 50.0})
: super(key: key);
#override
_MarqueeContinuousState createState() => _MarqueeContinuousState();
}
class _MarqueeContinuousState extends State<MarqueeContinuous> {
ScrollController _controller;
Timer _timer;
double _offset = 0.0;
#override
void initState() {
super.initState();
_controller = ScrollController(initialScrollOffset: _offset);
_timer = Timer.periodic(widget.duration, (timer) {
double newOffset = _controller.offset + widget.stepOffset;
if (newOffset != _offset) {
_offset = newOffset;
_controller.animateTo(_offset,
duration: widget.duration, curve: Curves.linear);
}
});
}
#override
void dispose() {
_timer.cancel();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _controller,
addAutomaticKeepAlives: false,
itemBuilder: (context, index) {
return widget.child;
});
}
}
Remove shrinkWrap and NeverScrollableScrollPhysics for the ListView or any Scrollable widgets that you want to add on-tapped widget to it .
Issue persists in flutter even in marquee package on tap doesn't work as intended.