如何将 ListView 添加到列?
How to add a ListView to a Column?
我想将我的 ListView.seperated
添加到 Column 中,以便在加载更多项目时可以在该 ListvView 下方添加 CircularProgressIndicator。我使用了 的建议,因此我制作了结构 Column -> Expanded -> ListVie,但是我遇到了错误并且无法加载列表:
======== Exception caught by rendering library =====================================================
RenderBox was not laid out: RenderRepaintBoundary#e8fad NEEDS-LAYOUT NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1929 pos 12: 'hasSize'
The relevant error-causing widget was:
The relevant error-causing widget was:
Column file:///C://lib/ui/pages/home/page/HomePage.dart:125:16
When the exception was thrown, this was the stack:
#0 RenderFlex.performLayout.<anonymous closure> (package:flutter/src/rendering/flex.dart:926:9)
#1 RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:929:6)
#2 RenderObject.layout (package:flutter/src/rendering/object.dart:1781:7)
#3 ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:54:11)
#4 RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:829:43)
Column file:///C:/.../.../lib/ui/pages/home/page/HomePage.dart:125:16
你能告诉我我做错了什么吗?此 AdvertisementTile
构建新的小部件,但它构建在另一列上。也许这是错误的?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:.../ui/pages/home/page/AdvertisementCard.dart';
import 'package:.../ui/pages/home/page/model/AdvertisementList.dart';
import '../../SizedBox.dart';
import 'AdvertisementProdRepository.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'model/AdvertisementList.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
@override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
late Future<AdvertisementList> _listOfItems;
final searchTextController = TextEditingController();
@override
void initState() {
super.initState();
jwt = widget.jwt;
_listOfItems = AdvertisementProdRepository.fetchAdvertisements(1);
}
@override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.only(left: 25.0, right: 25, top: 25),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
TextFormField(
controller: searchTextController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Szukaj',
fillColor: Color(0xffeeeeee),
filled: true),
),
buildSizedBox(20.0),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Najnowsze ogłoszenia',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
],
),
),
buildSizedBox(10.0),
FutureBuilder<AdvertisementList>(
future: _listOfItems,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return AdvertisementTile(advertisements: snapshot.data!);
}
},
),
],
),
),
),
);
}
class AdvertisementTile extends StatefulWidget {
final AdvertisementList advertisements;
AdvertisementTile({Key? key, required this.advertisements}) : super(key: key);
@override
State<StatefulWidget> createState() => AdvertisementTileState();
}
class AdvertisementTileState extends State<AdvertisementTile> {
AdvertisementLoadMoreStatus loadMoreStatus =
AdvertisementLoadMoreStatus.STABLE;
final ScrollController scrollController = new ScrollController();
late List<Advertisement> advertisements;
late int currentPageNumber;
bool _loading = false;
@override
void initState() {
advertisements = widget.advertisements.items;
currentPageNumber = widget.advertisements.pageNumber;
super.initState();
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: onNotification,
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
controller: scrollController,
itemCount: advertisements.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) {
return AdvertisementCard(data: advertisements[index]);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
),
),
],
),
);
}
/*child: Padding(
padding: const EdgeInsets.only(bottom: 28.0),*/
bool onNotification(ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (scrollController.position.maxScrollExtent > scrollController.offset &&
scrollController.position.maxScrollExtent - scrollController.offset <=
50) {
if (loadMoreStatus == AdvertisementLoadMoreStatus.STABLE) {
loadMoreStatus = AdvertisementLoadMoreStatus.LOADING;
_loading = true;
AdvertisementProdRepository.fetchAdvertisements(currentPageNumber + 1)
.then((advertisementObject) {
currentPageNumber = advertisementObject.pageNumber;
loadMoreStatus = AdvertisementLoadMoreStatus.STABLE;
setState(() => advertisements.addAll(advertisementObject.items));
_loading = false;
});
}
}
}
return true;
}
}
enum AdvertisementLoadMoreStatus { LOADING, STABLE }
所以 Column
的行为方式是,当你给它 2 个 children 时,flutter 引擎估计单个小部件将覆盖的 space ,然后它会相应地呈现这两个小部件。
Expanded
小部件告诉 flutter 获取一个小部件,然后覆盖该小部件可以容纳的 space 。在某些情况下,如果另一个小部件包含在 Container
中,则使用 Expanded
小部件是有意义的,这样 flutter 就知道您指的是剩余的 space。
ListView
是一个小部件,可以获取所有可用的 space。
因此,要解决您的渲染问题,您可以将 Listview
放在 Container
中,并给 Container
一个固定的 高度 然后 flutter知道如何管理屏幕并避免渲染问题。
如果你最终得到了任何东西,你仍然拥有大部分 UI,那么你只需要调整 [=14] 的 高度 =].
例如
Container(
height: 400,
child: ListView.separated(
itemBuilder: (context, index) {
return AdvertisementCard(
data: advertisements[index]);
},
separatorBuilder: (context, index) {
return SizedBox(
height: 10,
);
},
itemCount: reports.length),
)
您应该限制 Listview
的高度,因为 Column
想要获得其父项的所有可用高度(在您的情况下可能是 Scaffold
)。因此,作为 Listview
的 unbounded-height-widget 将导致此类错误。
解决方案:用一个Container
包裹Listview
一些高度。
我想将我的 ListView.seperated
添加到 Column 中,以便在加载更多项目时可以在该 ListvView 下方添加 CircularProgressIndicator。我使用了
======== Exception caught by rendering library =====================================================
RenderBox was not laid out: RenderRepaintBoundary#e8fad NEEDS-LAYOUT NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1929 pos 12: 'hasSize'
The relevant error-causing widget was:
The relevant error-causing widget was:
Column file:///C://lib/ui/pages/home/page/HomePage.dart:125:16
When the exception was thrown, this was the stack:
#0 RenderFlex.performLayout.<anonymous closure> (package:flutter/src/rendering/flex.dart:926:9)
#1 RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:929:6)
#2 RenderObject.layout (package:flutter/src/rendering/object.dart:1781:7)
#3 ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:54:11)
#4 RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:829:43)
Column file:///C:/.../.../lib/ui/pages/home/page/HomePage.dart:125:16
你能告诉我我做错了什么吗?此 AdvertisementTile
构建新的小部件,但它构建在另一列上。也许这是错误的?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:.../ui/pages/home/page/AdvertisementCard.dart';
import 'package:.../ui/pages/home/page/model/AdvertisementList.dart';
import '../../SizedBox.dart';
import 'AdvertisementProdRepository.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'model/AdvertisementList.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
@override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
late Future<AdvertisementList> _listOfItems;
final searchTextController = TextEditingController();
@override
void initState() {
super.initState();
jwt = widget.jwt;
_listOfItems = AdvertisementProdRepository.fetchAdvertisements(1);
}
@override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.only(left: 25.0, right: 25, top: 25),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
TextFormField(
controller: searchTextController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Szukaj',
fillColor: Color(0xffeeeeee),
filled: true),
),
buildSizedBox(20.0),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Najnowsze ogłoszenia',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
],
),
),
buildSizedBox(10.0),
FutureBuilder<AdvertisementList>(
future: _listOfItems,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return AdvertisementTile(advertisements: snapshot.data!);
}
},
),
],
),
),
),
);
}
class AdvertisementTile extends StatefulWidget {
final AdvertisementList advertisements;
AdvertisementTile({Key? key, required this.advertisements}) : super(key: key);
@override
State<StatefulWidget> createState() => AdvertisementTileState();
}
class AdvertisementTileState extends State<AdvertisementTile> {
AdvertisementLoadMoreStatus loadMoreStatus =
AdvertisementLoadMoreStatus.STABLE;
final ScrollController scrollController = new ScrollController();
late List<Advertisement> advertisements;
late int currentPageNumber;
bool _loading = false;
@override
void initState() {
advertisements = widget.advertisements.items;
currentPageNumber = widget.advertisements.pageNumber;
super.initState();
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: onNotification,
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
controller: scrollController,
itemCount: advertisements.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) {
return AdvertisementCard(data: advertisements[index]);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
),
),
],
),
);
}
/*child: Padding(
padding: const EdgeInsets.only(bottom: 28.0),*/
bool onNotification(ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (scrollController.position.maxScrollExtent > scrollController.offset &&
scrollController.position.maxScrollExtent - scrollController.offset <=
50) {
if (loadMoreStatus == AdvertisementLoadMoreStatus.STABLE) {
loadMoreStatus = AdvertisementLoadMoreStatus.LOADING;
_loading = true;
AdvertisementProdRepository.fetchAdvertisements(currentPageNumber + 1)
.then((advertisementObject) {
currentPageNumber = advertisementObject.pageNumber;
loadMoreStatus = AdvertisementLoadMoreStatus.STABLE;
setState(() => advertisements.addAll(advertisementObject.items));
_loading = false;
});
}
}
}
return true;
}
}
enum AdvertisementLoadMoreStatus { LOADING, STABLE }
所以 Column
的行为方式是,当你给它 2 个 children 时,flutter 引擎估计单个小部件将覆盖的 space ,然后它会相应地呈现这两个小部件。
Expanded
小部件告诉 flutter 获取一个小部件,然后覆盖该小部件可以容纳的 space 。在某些情况下,如果另一个小部件包含在 Container
中,则使用 Expanded
小部件是有意义的,这样 flutter 就知道您指的是剩余的 space。
ListView
是一个小部件,可以获取所有可用的 space。
因此,要解决您的渲染问题,您可以将 Listview
放在 Container
中,并给 Container
一个固定的 高度 然后 flutter知道如何管理屏幕并避免渲染问题。
如果你最终得到了任何东西,你仍然拥有大部分 UI,那么你只需要调整 [=14] 的 高度 =].
例如
Container(
height: 400,
child: ListView.separated(
itemBuilder: (context, index) {
return AdvertisementCard(
data: advertisements[index]);
},
separatorBuilder: (context, index) {
return SizedBox(
height: 10,
);
},
itemCount: reports.length),
)
您应该限制 Listview
的高度,因为 Column
想要获得其父项的所有可用高度(在您的情况下可能是 Scaffold
)。因此,作为 Listview
的 unbounded-height-widget 将导致此类错误。
解决方案:用一个Container
包裹Listview
一些高度。