Я разрабатываю приложение Flutter и сталкиваюсь с ошибкой «переполнение нижней части». Я знаю, что подобных вопросов много, но в моем случае ни один из них не помог. Вот мой код:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('title'),
centerTitle: false,
automaticallyImplyLeading: false,
),
body: Builder(builder: (context) {
return CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 25.0),
child: DepotInventarSliderFlex(),
),
const Divider(
thickness: 0.5,
color: Colors.black,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(bottom: 10.0),
child: Text(
style: TextStyle(
fontSize: 23.0,
),
'some text',
),
),
Text(
style: TextStyle(
fontSize: 15.0,
),
'some text',
),
],
),
),
TabBar(
controller: tabController,
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
],
),
),
SliverPersistentHeader(
delegate: _StickyTabBarDelegate(
child: const CartFloatingButton(),
),
pinned: true,
),
SliverFillRemaining(
child: TabBarView(
controller: tabController,
children: [
_getTabContentAt(0),
_getTabContentAt(1),
_getTabContentAt(2),
],
),
),
],
);
}),
);
}
Widget _getTabContentAt(int index) {
List<Widget> tabContents = [
Column(
children: [
ExpandableButtonList(
buttons: testData,
),
],
),
Column(
children: [
ExpandableButtonList(
buttons: testData,
),
],
),
const Card(
margin: EdgeInsets.all(16.0),
child: Center(child: Text('...')),
),
];
return tabContents[index];
}
}
class _ExpandableButtonListState extends State<ExpandableButtonList> {
int _selectedIndex = -1;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
width: double.infinity,
// height: double.infinity,
// height: MediaQuery.of(context).size.height,
child: Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
spacing: 8.0,
children: [
for (var i = 0; i < widget.buttons.length; i++)
TerminalTapBarButton(
label: widget.buttons[i].label,
isSelected: _selectedIndex == i,
onPressed: () {
setState(() {
_selectedIndex = _selectedIndex == i ? -1 : i;
});
},
)
],
),
),
if (_selectedIndex != -1)
Items(items: widget.buttons[_selectedIndex].items),
],
);
}
}
Я получаю ошибку «нижнее переполнение», когда открываю контент TabBarView(), который немного длиннее.
Я попробовал обернуть ExpandableButtonList() с помощью Flexible и Expanded, но ошибка все равно есть.
SingleChildScrollView() не помогает, потому что мне нужно, чтобы прокручивалось все содержимое страницы вместе, а не содержимое вкладки отдельно.
Как я могу решить эту проблему переполнения, сохранив текущую структуру макета?
Любая помощь или предложения будут очень признательны. Спасибо!
Вот несколько фотографий; Я надеюсь, что они будут полезны.
Прежде чем нажать на контент:
После нажатия на контент:
РЕШЕНИЕ:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('Title'),
centerTitle: false,
automaticallyImplyLeading: false,
),
body: Builder(builder: (context) {
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return <Widget>[
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(bottom: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Some text',
style: TextStyle(
fontSize: 23.0,
),
),
Icon(
Icons.info_outline_rounded,
size: 25,
),
],
),
),
Text(
'Some text',
style: TextStyle(
fontSize: 15.0,
),
),
],
),
),
),
SliverOverlapAbsorber(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverPersistentHeader(
delegate: CartFloatingButtonDelegate(
child: const CartFloatingButton(),
),
pinned: true,
),
),
SliverToBoxAdapter(
child: TabBar(
controller: tabController,
isScrollable: true,
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
];
},
body: TabBarView(
controller: tabController,
children: [
_getTabContentAt(0),
_getTabContentAt(1),
_getTabContentAt(2),
],
),
);
}),
);
}
@ValentinVignal Я добавил две картинки; Я надеюсь, что они будут полезны.
На основе предоставленного вами фрагмента кода вы можете решить проблему «переполнения нижней части», переключившись с CustomScrollView на NestedScrollView. Это изменение позволяет вам поддерживать прокручиваемую страницу, на которой все содержимое плавно прокручивается вместе, включая содержимое TabBarView.
Я протестировал это локально, и вот полный код, основанный на ваших требованиях к макету. Этот код должен работать для ваших нужд. Вот еще изображение:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
_StickyTabBarDelegate({required this.child});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return child;
}
@override
double get maxExtent => 56.0;
@override
double get minExtent => 56.0;
@override
bool shouldRebuild(covariant _StickyTabBarDelegate oldDelegate) {
return false;
}
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
super.initState();
tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('title'),
centerTitle: false,
automaticallyImplyLeading: false,
),
body: Builder(builder: (context) {
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return <Widget>[
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(bottom: 25.0),
),
),
SliverToBoxAdapter(
child: const Divider(
thickness: 0.5,
color: Colors.black,
),
),
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Text(
'Some text',
style: TextStyle(
fontSize: 23.0,
),
),
),
Text(
'Some text',
style: TextStyle(
fontSize: 15.0,
),
),
],
),
),
),
SliverToBoxAdapter(
child: TabBar(
controller: tabController,
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
];
},
body: TabBarView(
controller: tabController,
children: [
_getTabContentAt(0),
_getTabContentAt(1),
_getTabContentAt(2),
],
),
);
}),
);
}
Widget _getTabContentAt(int index) {
List<Widget> tabContents = [
ExpandableButtonList(
buttons: testData,
),
Column(
children: [
ExpandableButtonList(
buttons: testData,
),
],
),
const Card(
margin: EdgeInsets.all(16.0),
child: Center(child: Text('...')),
),
];
return tabContents[index];
}
}
class ExpandableButtonList extends StatefulWidget {
final List<TestData> buttons;
ExpandableButtonList({required this.buttons});
@override
_ExpandableButtonListState createState() => _ExpandableButtonListState();
}
class _ExpandableButtonListState extends State<ExpandableButtonList> {
int _selectedIndex = -1;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
width: double.infinity,
child: Wrap(
alignment: WrapAlignment.start,
spacing: 8.0,
children: List.generate(
widget.buttons.length,
(index) => TerminalTapBarButton(
label: widget.buttons[index].label,
isSelected: _selectedIndex == index,
onPressed: () {
setState(() {
_selectedIndex = _selectedIndex == index ? -1 : index;
});
},
),
),
),
),
if (_selectedIndex != -1) Expanded(child: Items(items: widget.buttons[_selectedIndex].items)),
],
);
}
}
class TestData {
final String label;
final List<String> items;
TestData({required this.label, required this.items});
}
List<TestData> testData = [
TestData(label: 'Button 1', items: List.generate(50, (index) => 'Item ${index + 1}')),
TestData(label: 'Button 2', items: ['Item 3', 'Item 4']),
TestData(label: 'Button 3', items: ['Item 5', 'Item 6']),
];
class TerminalTapBarButton extends StatelessWidget {
final String label;
final bool isSelected;
final VoidCallback onPressed;
TerminalTapBarButton({required this.label, required this.isSelected, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(isSelected ? Colors.blue : Colors.grey),
),
child: Text(label),
);
}
}
class Items extends StatelessWidget {
final List<String> items;
Items({required this.items});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: items.map((item) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item),
Container(
color: Colors.grey[300],
width: double.infinity,
height: 100,
)
],
)).toList(),
),
),
);
}
}
Что я изменил:
Большое спасибо за Вашу помощь. Ваша структура кода мне очень помогла. Я просто добавил несколько недостающих элементов.
Можете ли вы поделиться изображением того, где происходит это переполнение?