I am using a combination of "BottomNavigationBar" and "PageView" for navigation in my app. The user can either swipe to the next page or use the navigation bar.
On one of my pages, I would like to use a gesture detector that handles pan gestures, both vertically and horizontally.
I can't find a way to override the PageView's gesture detection with the nested GestureDetector. This means only vertical pan gestures are handled, as the horizontal ones are occupied by the PageView.
How can I disable / override the PageViews gesture detection for only that page or only the widget, without completely disabling the PageViews scroll physics?
I have created a simplified version of my App to isolate the issue, and attached a video of the problem below.
Any help would be greatly appreciated!
Here is the code inside my main.dart:
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(
primarySwatch: Colors.blue,
),
home: GestureIssueExample(),
);
}
}
class GestureIssueExample extends StatefulWidget {
GestureIssueExample({Key key}) : super(key: key);
@override
_GestureIssueExampleState createState() => _GestureIssueExampleState();
}
class _GestureIssueExampleState extends State<GestureIssueExample> {
int _navigationIndex;
double _xLocalValue;
double _yLocalValue;
PageController _pageController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
bottomNavigationBar: _buildBottomNavigationBar(),
backgroundColor: Colors.white,
body: PageView(
controller: _pageController,
onPageChanged: _onNavigationPageChanged,
children: [
//Just a placeholder to represent a page to the left of the "swipe cards" widget
_buildSamplePage("Home"),
//Center child of 'PageView', contains a GestureDetector that handles Pan Gestures
//Thanks to the page view however, only vertical pan gestures are detected, while both horizontal and vertical gestures
//need to be handled...
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Local X: ${_xLocalValue.toString()}\nLocal Y: ${_yLocalValue.toString()}"),
GestureDetector(
onPanStart: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
onPanUpdate: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
height: 100.0,
color: Colors.red,
alignment: Alignment.center,
child: Text("Slidable Surface",
style: TextStyle(color: Colors.white)),
),
),
],
),
),
//Just a placeholder to represent a page to the right of the "swipe cards" widget
_buildSamplePage("Settings"),
],
),
);
}
@override
void initState() {
super.initState();
this._navigationIndex = 0;
this._pageController = PageController(
initialPage: _navigationIndex,
);
}
Widget _buildSamplePage(String text) {
// This simply returns a container that fills the page,
// with a text widget in its center.
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.grey[900],
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
color: Colors.white, fontSize: 30.0, fontWeight: FontWeight.bold),
),
);
}
Widget _buildBottomNavigationBar() {
//Returns the bottom navigation bar for the scaffold
return BottomNavigationBar(
backgroundColor: Colors.grey[900],
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.white,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.check_box_outline_blank), label: "Cards"),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined), label: "Settings"),
],
currentIndex: _navigationIndex,
onTap: _onNavigationPageChanged,
);
}
void _onNavigationPageChanged(int newIndex) {
//Set the new navigation index for the nav bar
setState(() => this._navigationIndex = newIndex);
//Animate to the selected page
_pageController.animateToPage(
newIndex,
curve: Curves.easeInOut,
duration: Duration(microseconds: 100),
);
}
}