Flutter PageView内部有可滚动的SlidingUpPanel –> 糟糕的状态: 元素太多

huangapple go评论57阅读模式
英文:

Flutter PageView with Scrollables inside SlidingUpPanel --> Bad state: Too many elements

问题

I'll provide a translation of the non-code portion of your text:

我遇到了与嵌套可滚动小部件相关的问题。
这里有一个演示项目。

我有一个PageView,其中包含2个页面,每个页面都包含一个CustomScrollView
这个PageView本身位于SlidingUpPanel的滑动面板内。

它运行得很完美,但日志中充斥着太多的"Bad state: Too many elements"异常...

这是一个视频(更复杂的示例,但结构基本与测试项目相同):

Flutter PageView内部有可滚动的SlidingUpPanel –> 糟糕的状态: 元素太多

UI的结构如下:

SlidingUpPanel > PageView > 2个ListViews(或CustomScrollViews)

虽然它确实运行良好,但这让人感到不安... 在阅读了许多相关帖子后(例如这篇),我认为这是因为我将相同的ScrollController附加到了PageView内部的两个可滚动部分(以及包围SlidingUpPanel的部分,这是正常的)。

然而,它显然可以工作。那么... 为什么要将其记录为错误呢?我真的不愿意发布潜在问题的代码...

以下是代码部分,我将尝试实施修复,只将scrollController切换到PageView中的"活动"页面,并稍后发布。

英文:

EDIT: I have simplified the example and following description.

I am facing an issue related to nested scrollable widgets.
Here is a demo project.

I have a PageView which contains 2 pages, each one containing a
CustomScrollView.
This PageViewis itself inside the sliding panel of a SlidingUpPanel.

It works perfectly, but the logs are flooded with see a lot of "Bad state: Too many elements" exceptions...

Here is a video (of a more complex example, but the structure is basically the same as in the test project):

Flutter PageView内部有可滚动的SlidingUpPanel –> 糟糕的状态: 元素太多

The structure of the UI is:

SlidingUpPanel > PageView > 2 ListViews (or CustomScrollViews)

While it really works perfectly, that's annoying... After reading many related posts (such as this one), I think that it's caused by the fact that I attach the same ScrollController to both scrollables inside the PageView (as well as to the enclosing SlidingUpPanel, which is normal).

However, it obviously works. So... Why log this as an error? I feel really uncomfortable relasing code with such potential issues...

Here is the code:

import 'package:flutter/material.dart';
import 'package:sliding_up_panel2/sliding_up_panel2.dart';
import 'package:flutter/services.dart';
import 'package:flutter_map/flutter_map.dart';
void main() => runApp(SlidingUpPanelExample());
class SlidingUpPanelExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: Colors.grey[200],
systemNavigationBarIconBrightness: Brightness.dark,
systemNavigationBarDividerColor: Colors.black,
));
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'SlidingUpPanel Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _panelHeightOpen = 0;
final double _panelHeightClosed = 95.0;
late ScrollController scrollController;
late PanelController panelController;
@override
void initState() {
scrollController = ScrollController();
panelController = PanelController();
super.initState();
}
@override
Widget build(BuildContext context) {
_panelHeightOpen = MediaQuery.of(context).size.height * .80;
return Material(
child: Stack(
alignment: Alignment.topCenter,
children: <Widget>[
SlidingUpPanel(
controller: panelController,
scrollController: scrollController,
maxHeight: _panelHeightOpen,
minHeight: _panelHeightClosed,
parallaxEnabled: true,
parallaxOffset: .5,
body: _body(),
panelBuilder: () => _panel(),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(18.0),
topRight: Radius.circular(18.0)),
),
//the SlidingUpPanel Title
Positioned(
top: 52.0,
child: Container(
padding: const EdgeInsets.fromLTRB(24.0, 18.0, 24.0, 18.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
boxShadow: const [
BoxShadow(
color: Color.fromRGBO(0, 0, 0, .25), blurRadius: 16.0)
],
),
child: const Text(
"SlidingUpPanel Example",
style: TextStyle(fontWeight: FontWeight.w500),
),
),
),
],
),
);
}
Widget _panel() {
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: PageView(
children: [
createCustomScrollView(0),
createCustomScrollView(1),
],
));
}
CustomScrollView createCustomScrollView(int index) {
var cityName = index == 0 ? "Pittsburgh" : "Boston";
return CustomScrollView(
controller: scrollController,
// primary: false,
slivers: [
SliverList(
delegate: SliverChildListDelegate(<Widget>[
_vertSpacer(12.0),
_panelHandle(),
_vertSpacer(18.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
cityName,
style: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 24.0,
),
),
],
),
_vertSpacer(36.0),
Container(
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("About",
style: TextStyle(
fontWeight: FontWeight.w600,
)),
const SizedBox(
height: 12.0,
),
Text(
"""
$cityName is a city in the state of Pennsylvania in the United States, and is the county seat of Allegheny County. A population of about 302,407 (2018) residents live within the city limits, making it the 66th-largest city in the U.S. The metropolitan population of 2,324,743 is the largest in both the Ohio Valley and Appalachia, the second-largest in Pennsylvania (behind Philadelphia), and the 27th-largest in the U.S.\n\$cityName is located in the southwest of the state, at the confluence of the Allegheny, Monongahela, and Ohio rivers. Pittsburgh is known both as "the Steel City" for its more than 300 steel-related businesses and as the "City of Bridges" for its 446 bridges. The city features 30 skyscrapers, two inclined railways, a pre-revolutionary fortification and the Point State Park at the confluence of the rivers. The city developed as a vital link of the Atlantic coast and Midwest, as the mineral-rich Allegheny Mountains made the area coveted by the French and British empires, Virginians, Whiskey Rebels, and Civil War raiders.\n\nAside from steel, Pittsburgh has led in manufacturing of aluminum, glass, shipbuilding, petroleum, foods, sports, transportation, computing, autos, and electronics. For part of the 20th century, Pittsburgh was behind only New York City and Chicago in corporate headquarters employment; it had the most U.S. stockholders per capita. Deindustrialization in the 1970s and 80s laid off area blue-collar workers as steel and other heavy industries declined, and thousands of downtown white-collar workers also lost jobs when several Pittsburgh-based companies moved out. The population dropped from a peak of 675,000 in 1950 to 370,000 in 1990. However, this rich industrial history left the area with renowned museums, medical centers, parks, research centers, and a diverse cultural district.\n\nAfter the deindustrialization of the mid-20th century, Pittsburgh has transformed into a hub for the health care, education, and technology industries. Pittsburgh is a leader in the health care sector as the home to large medical providers such as University of Pittsburgh Medical Center (UPMC). The area is home to 68 colleges and universities, including research and development leaders Carnegie Mellon University and the University of Pittsburgh. Google, Apple Inc., Bosch, Facebook, Uber, Nokia, Autodesk, Amazon, Microsoft and IBM are among 1,600 technology firms generating \$20.7 billion in annual Pittsburgh payrolls. The area has served as the long-time federal agency headquarters for cyber defense, software engineering, robotics, energy research and the nuclear navy. The nation's eighth-largest bank, eight Fortune 500 companies, and six of the top 300 U.S. law firms make their global headquarters in the area, while RAND Corporation (RAND), BNY Mellon, Nova, FedEx, Bayer, and the National Institute for Occupational Safety and Health (NIOSH) have regional bases that helped $cityName become the sixth-best area for U.S. job growth.
""",
softWrap: true,
),
],
),
),
const SizedBox(
height: 24,
),
]))
],
);
}
SizedBox _vertSpacer(double height) {
return SizedBox(
height: height,
);
}
Row _panelHandle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 30,
height: 5,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: const BorderRadius.all(Radius.circular(12.0))),
),
],
);
}
Widget _body() {
return FlutterMap(
options: MapOptions(
// center: LatLng(40.441589, -80.010948),
zoom: 13,
maxZoom: 15,
),
children: [
TileLayer(
urlTemplate: "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png"),
],
);
}
}

I'll try to implement a fix where I'll switch the scrollController to only the "active" page in the PageView and post it later.

答案1

得分: 0

One error is expecting is using single ScrollController on multiple CustomScrollView. It is coming from createCustomScrollView. Setting primary: false will help

CustomScrollView createCustomScrollView(ScrollController sc) {
    return CustomScrollView(
      controller: sc,
      primary: false,
英文:

One error is expecting is using single ScrollController on multiple CustomScrollView. It is coming from createCustomScrollView. Setting primary: false will help

CustomScrollView createCustomScrollView(ScrollController sc) {
    return CustomScrollView(
      controller: sc,
      primary: false,

答案2

得分: 0

I have implemented a quick fix, visible in this branch.

Basically, I have added a ValueNotifier that allows associating the scrollController to the active page:

    return ValueListenableBuilder(
      valueListenable: activePageListenable,
      builder: (BuildContext context, value, Widget? child) {
        return CustomScrollView(
          controller: value == index ? scrollController : null,

The ValueNotifier is defined as:

  ValueNotifier<int> activePageListenable = ValueNotifier(0);

and the PageView reacts to page change events as follows:

        child: PageView(
          onPageChanged: (index) {
            activePageListenable.value = index;
          },

Maybe there are smarter solutions, but for now, I'm happy with that...

英文:

OK. I have implemented a quick fix, visible in this branch.

Basically, I have added a ValueNotifier that allows associating the scrollController to the active page:

    return ValueListenableBuilder(
valueListenable: activePageListenable,
builder: (BuildContext context, value, Widget? child) {
return CustomScrollView(
controller: value == index ? scrollController : null,

The ValueNotifier is defined as:

  ValueNotifier&lt;int&gt; activePageListenable = ValueNotifier(0);

and the PageView reacts to page change events as follows:

        child: PageView(
onPageChanged: (index) {
activePageListenable.value = index;
},

Maybe there are smarter solutions, but for now, I'm happy with that...

huangapple
  • 本文由 发表于 2023年4月17日 21:37:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76035781.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定