Reputation: 1244
I get the following error when I try to invoke _pagecontroller.jumpToPage
or _pagecontroller.animateToPage
:
Exception has occurred. _AssertionError ('package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 157 pos 12: '_positions.isNotEmpty': ScrollController not attached to any scroll views.)
it throws the error in this block of code:
void navigateToPage(int index) {
log(index.toString());
_pageController.jumpToPage( <----- here
index,
// duration: Duration(milliseconds: 300),
// curve: Curves.linear,
);
}
I'm trying to learn flutter and not really familiar with the terminology, but what I have is the main.dart file that has the following stack for the build widget:
Widget build(BuildContext context) {
return PageControllerManager(
child: Scaffold(
appBar: AppBar(
elevation: 8.0,
backgroundColor: drawerBackgroundColor,
title: const Text(
"Photographers Assistant",
),
),
body: Stack(
children: [
PageView.builder(
controller: PageControllerProvider.of(context)?.pageController,
onPageChanged:
PageControllerProvider.of(context)?.onPageChanged,
physics: const NeverScrollableScrollPhysics(),
// onPageChanged: (int page) {
// setState(() {
// _activePage = page;
// });
// },
itemCount: _pages.length,
itemBuilder: (BuildContext context, int index) {
return _pages[index % _pages.length];
}),
const CollapsingNavigationDrawer()
],
),
),
);
I also have a collapsing navigation drawer that has the following on-tap:
onTap: () {
navigationItems[counter].onTap();
setState(() {
currentSelectedIndex = counter;
});
PageControllerProvider.of(context)
?.navigateToPage(counter);
if i comment the following in the navigation_drawer_widget.dart file, the error goes away but of course no page change happens:
PageControllerProvider.of(context)
?.navigateToPage(counter);
When the tap happens it throws that error and the new "page" is not loaded into the body of the main.dart file. I'm not trying to scroll or anything, just trying to load in another page when I click the items in the navigation drawer. What am i doing wrong?
Edit: If I wrap the animateToPage
in a hasclients if, the error goes away, meaning i guess it has no clients, but still stuck
void navigateToPage(int index) {
log(index.toString());
if (_pageController.hasClients) {
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.linear,
);
}
}
Here's my entire page_controller.dart:
import 'dart:developer';
import 'package:flutter/material.dart';
class PageControllerManager extends StatefulWidget {
final Widget child;
const PageControllerManager({Key? key, required this.child})
: super(key: key);
//get child => null;
@override
_PageControllerManagerState createState() => _PageControllerManagerState();
}
class _PageControllerManagerState extends State<PageControllerManager> {
PageController _pageController = PageController(initialPage: 0);
int _currentPageIndex = 0;
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void onPageChanged(int index) {
setState(() {
_currentPageIndex = index;
});
}
void navigateToPage(int index) {
log(index.toString());
if (_pageController.hasClients) {
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.linear,
);
}
}
@override
Widget build(BuildContext context) {
return PageControllerProvider(
pageController: _pageController,
currentPageIndex: _currentPageIndex,
onPageChanged: onPageChanged,
navigateToPage: navigateToPage,
child: widget.child,
);
}
}
class PageControllerProvider extends InheritedWidget {
final PageController pageController;
final int currentPageIndex;
final ValueChanged<int> onPageChanged;
final ValueChanged<int> navigateToPage;
final Widget child;
PageControllerProvider({
Key? key,
required this.pageController,
required this.currentPageIndex,
required this.onPageChanged,
required this.navigateToPage,
required this.child,
}) : super(key: key, child: child);
static PageControllerProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<PageControllerProvider>();
}
@override
bool updateShouldNotify(PageControllerProvider oldWidget) {
return pageController != oldWidget.pageController ||
currentPageIndex != oldWidget.currentPageIndex;
}
}
And my collapsing_nvagation_drawer.dart:
import 'package:photographers_assistant/model/page_controller.dart';
import '../custom_navigation_drawer.dart';
import 'package:flutter/material.dart';
class CollapsingNavigationDrawer extends StatefulWidget {
//final PageController controller;
// const CollapsingNavigationDrawer({super.key, required this.controller});
const CollapsingNavigationDrawer({super.key});
@override
CollapsingNavigationDrawerState createState() {
return CollapsingNavigationDrawerState();
}
}
class CollapsingNavigationDrawerState extends State<CollapsingNavigationDrawer>
with SingleTickerProviderStateMixin {
double maxWidth = 250;
double minWidth = 70;
bool isCollapsed = false;
late AnimationController _animationController;
late Animation<double> widthAnimation;
int currentSelectedIndex = 0;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
widthAnimation = Tween<double>(begin: maxWidth, end: minWidth)
.animate(_animationController);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, widget) => getWidget(context, widget),
);
}
Widget getWidget(context, widget) {
final navigateToPage = PageControllerProvider.of(context)?.navigateToPage;
return Material(
elevation: 80.0,
child: Container(
width: widthAnimation.value,
color: drawerBackgroundColor,
child: Column(
children: <Widget>[
CollapsingListTile(
title: 'Techie',
icon: Icons.person,
animationController: _animationController,
),
const Divider(
color: Colors.grey,
height: 20.0,
),
Expanded(
child: ListView.separated(
separatorBuilder: (context, counter) {
return const Divider(height: 12.0, color: Colors.transparent);
},
itemBuilder: (context, counter) {
return CollapsingListTile(
// onTap: () {
// setState(() {
// currentSelectedIndex = counter;
// log(currentSelectedIndex.toString());
// });
// },
isSelected: currentSelectedIndex == counter,
onTap: () {
navigationItems[counter].onTap();
PageControllerProvider.of(context)
?.navigateToPage(counter);
setState(() {
currentSelectedIndex = counter;
});
//Navigator.pop(context);
//navigateToPage ?? (currentSelectedIndex) {};
//isCollapsed = !isCollapsed;
//_animationController.forward();
(counter);
////Navigator.pushNamed(context, '/models');
//Navigator.of(context).pushNamed('/models');
},
title: navigationItems[counter].title,
icon: navigationItems[counter].icon,
animationController: _animationController,
);
},
itemCount: navigationItems.length,
),
),
InkWell(
onTap: () {
setState(() {
isCollapsed = !isCollapsed;
isCollapsed
? _animationController.forward()
: _animationController.reverse();
});
},
child: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _animationController,
color: selectedColor,
size: 50.0,
),
),
const SizedBox(
height: 50.0,
),
],
),
),
);
}
}
and finally, the main.dart:
import 'package:flutter/material.dart';
import 'package:photographers_assistant/custom_navigation_drawer.dart';
import 'package:photographers_assistant/page/home.dart';
import 'package:photographers_assistant/page/models.dart';
import 'package:photographers_assistant/page/photoshoots.dart';
import 'package:photographers_assistant/page/studios.dart';
import 'package:photographers_assistant/model/page_controller.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Navigation Drawer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// declare ana initialize the page controller
final PageController _pageController = PageController(initialPage: 1);
int _activePage = 0;
final List<Widget> _pages = [Home(), Photoshoots(), Models(), Studios()];
@override
Widget build(BuildContext context) {
return PageControllerManager(
child: Scaffold(
appBar: AppBar(
elevation: 8.0,
backgroundColor: drawerBackgroundColor,
title: const Text(
"Photographers Assistant",
),
),
body: Stack(
children: [
PageView.builder(
controller: PageControllerProvider.of(context)?.pageController,
onPageChanged:
PageControllerProvider.of(context)?.onPageChanged,
physics: const NeverScrollableScrollPhysics(),
// onPageChanged: (int page) {
// setState(() {
// _activePage = page;
// });
// },
itemCount: _pages.length,
itemBuilder: (BuildContext context, int index) {
return _pages[index % _pages.length];
}),
const CollapsingNavigationDrawer()
],
),
),
);
}
}
Upvotes: 0
Views: 44
Reputation: 1244
Ok I got it working using a provider, and via Chat GPT.
https://chatgpt.com/share/5d052ce2-06d0-46f4-a1a7-aca033927615
Basically the final code was:
Main.dart:
import 'package:flutter/material.dart';
import 'package:app/custom_navigation_drawer.dart';
import 'package:app/model/page_controller.dart';
import 'package:app/page/home.dart';
import 'package:app/page/apge1.dart';
import 'package:app/page/page2.dart';
import 'package:papp/page/settings.dart';
import 'package:app/page/page3.dart';
import 'package:provider/provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return PageControllerWrapper(
child: MaterialApp(
title: 'Custom Navigation Drawer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: MyHomePage(),
));
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
//State<MyHomePage> createState() => _MyHomePageState();
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// declare ana initialize the page controller
//final PageController _pageController = PageController(initialPage: 0);
//int _activePage = 0;
final List<Widget> _pages = [
Home(),
Page1(),
Page2(),
Page3(),
Settings()
];
@override
Widget build(BuildContext context) {
final pageControllerProvider = Provider.of<PageControllerProvider>(context);
return Scaffold(
appBar: AppBar(
elevation: 8.0,
backgroundColor: drawerBackgroundColor,
title: const Text(
"App Title",
),
),
body: Stack(
children: [
PageView.builder(
controller: pageControllerProvider.pageController,
physics: const NeverScrollableScrollPhysics(),
itemCount: _pages.length,
itemBuilder: (context, index) {
return _pages[index % _pages.length];
}),
const CollapsingNavigationDrawer()
],
),
);
}
}
The collapsing_navigation_drawer.dart:
import 'package:flutter/material.dart';
import 'package:app/model/page_controller.dart';
import 'package:provider/provider.dart';
import '../custom_navigation_drawer.dart';
class CollapsingNavigationDrawer extends StatefulWidget {
const CollapsingNavigationDrawer({Key? key}) : super(key: key);
@override
CollapsingNavigationDrawerState createState() {
return CollapsingNavigationDrawerState();
}
}
class CollapsingNavigationDrawerState extends State<CollapsingNavigationDrawer>
with SingleTickerProviderStateMixin {
double maxWidth = 250;
double minWidth = 70;
bool isCollapsed = false;
late AnimationController _animationController;
late Animation<double> widthAnimation;
int currentSelectedIndex = 0;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
widthAnimation = Tween<double>(begin: maxWidth, end: minWidth)
.animate(_animationController);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, widget) => getWidget(context, widget),
);
}
Widget getWidget(context, widget) {
final pageControllerProvider = Provider.of<PageControllerProvider>(context);
return Material(
elevation: 80.0,
child: Container(
width: widthAnimation.value,
color: drawerBackgroundColor,
child: Column(
children: <Widget>[
CollapsingListTile(
title: 'Techie',
icon: Icons.person,
animationController: _animationController,
),
const Divider(
color: Colors.grey,
height: 20.0,
),
Expanded(
child: ListView.separated(
separatorBuilder: (context, counter) {
return const Divider(height: 12.0, color: Colors.transparent);
},
itemBuilder: (context, counter) {
return CollapsingListTile(
isSelected: currentSelectedIndex == counter,
onTap: () {
navigationItems[counter].onTap();
pageControllerProvider.pageController.jumpToPage(counter);
setState(() {
currentSelectedIndex = counter;
});
},
title: navigationItems[counter].title,
icon: navigationItems[counter].icon,
animationController: _animationController,
);
},
itemCount: navigationItems.length,
),
),
InkWell(
onTap: () {
setState(() {
isCollapsed = !isCollapsed;
isCollapsed
? _animationController.forward()
: _animationController.reverse();
});
},
child: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _animationController,
color: selectedColor,
size: 50.0,
),
),
const SizedBox(
height: 50.0,
),
],
),
),
);
}
}
and the page_controller.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PageControllerProvider with ChangeNotifier {
final PageController _pageController = PageController();
PageController get pageController => _pageController;
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
}
class PageControllerWrapper extends StatelessWidget {
final Widget child;
PageControllerWrapper({required this.child});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => PageControllerProvider(),
child: child,
);
}
}
posting these because it might help someone else, and because it's a way to give back to the community that's helped me, and as someone new to the language, sometimes it's hard to get context for things, so posting the full files might help others.
Thanks!
Upvotes: 0