我在渲染具有動態高度的ListViewBuilder的專案時遇到了很多問題,這意味著高度將根據我要渲染的專案數量而相應改變。此外,listview 是NeverScrollable,因為我用SingleChildScrollView包裝了它,以便將 listView 與其他小部件一起滾動作為唯一的小部件。最后,listViewBuilder 的shrinkWrap設定為 True。問題_是如果我將包含 ListViewBuilder 的 TabBarView 的高度固定為大于所有專案總和高度的值,那么我將留下空白!另一方面,如果高度較小,則某些專案不會被渲染!!!您對此有什么解決方案嗎?謝謝!
代碼下方:
- 這是主頁:我有一個列,其中包含一個用于搜索欄的容器和一個FoodPageView()(最后一個已展開)作為子項:
const HomePage({
Key? key,
}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: 1,
selectedIconTheme: const IconThemeData(
color: Colors.blue,
),
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: 'Cart',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Me',
),
],
),
body: SafeArea(
child: Column(
children: [
Container(
margin: const EdgeInsets.only(
top: 20,
left: 10,
right: 10,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
filled: true,
fillColor: Colors.blue[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(
width: 0,
style: BorderStyle.none,
),
),
contentPadding: const EdgeInsets.only(
left: 20,
),
hintText: "Search store",
),
),
),
Container(
margin: const EdgeInsets.only(
left: 10,
),
height: Dimensions.height50,
width: Dimensions.width50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.amberAccent,
),
child: const Icon(
Icons.search_outlined,
),
)
],
),
),
SizedBox(
height: Dimensions.height20,
),
const Expanded(child: FoodPageView()),
],
),
),
);
}
}
- FoodPageView ()實作:它包含一個帶有子的 Column 一個PageViewBuilder,一個DotsIndicator,最后是一個自定義NavigationBarTab()
const FoodPageView({Key? key}) : super(key: key);
@override
State<FoodPageView> createState() => _FoodPageViewState();
}
class _FoodPageViewState extends State<FoodPageView> {
final PageController _pageController = PageController(
viewportFraction: 0.85,
);
double _currPageValue = 0.0;
final double _scaleFactor = 0.8;
final int _height = 300;
@override
void initState() {
super.initState();
_pageController.addListener(() {
setState(() {
_currPageValue = _pageController.page!;
});
});
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(
children: [
SizedBox(
height: Dimensions.height290,
child: BlocBuilder<ProductsBloc, ProductsState>(
builder: (context, state) {
final List<Product> productsPromos = state.products
.where((product) => product.hasPromo == true)
.toList();
return PageView.builder(
controller: _pageController,
physics: const ScrollPhysics(),
itemCount: productsPromos.length,
itemBuilder: ((context, index) {
Matrix4 matrix = Matrix4.identity();
if (index == _currPageValue.floor()) {
final double currScale =
1 - (_currPageValue - index) * (1 - _scaleFactor);
final double currTrans = _height * (1 - currScale) / 2;
matrix = Matrix4.diagonal3Values(1, currScale, 1)
..setTranslationRaw(0, currTrans, 0);
} else if (index == _currPageValue.floor() 1) {
final double currScale = _scaleFactor
(_currPageValue - index 1) * (1 - _scaleFactor);
final double currTrans = _height * (1 - currScale) / 2;
matrix = Matrix4.diagonal3Values(1, currScale, 1)
..setTranslationRaw(0, currTrans, 0);
} else if (index == _currPageValue.floor() - 1) {
final double currScale =
1 - (_currPageValue - index) * (1 - _scaleFactor);
final double currTrans = _height * (1 - currScale) / 2;
matrix = Matrix4.diagonal3Values(1, currScale, 1)
..setTranslationRaw(0, currTrans, 0);
} else {
const double currScale = 0.8;
final double currTrans = _height * (1 - _scaleFactor) / 2;
matrix = Matrix4.diagonal3Values(1, currScale, 1)
..setTranslationRaw(0, currTrans, 0);
}
return Transform(
transform: matrix,
child: Stack(
children: [
Container(
height: Dimensions.height200,
margin: const EdgeInsets.only(
right: 10,
),
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage(productsPromos[index].image),
),
borderRadius: BorderRadius.circular(20),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: Dimensions.height100,
margin: const EdgeInsets.only(
left: 30,
right: 30,
bottom: 15,
),
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: Colors.grey,
blurRadius: 5,
offset: Offset(0, 5),
),
BoxShadow(
color: Colors.white,
blurRadius: 0,
offset: Offset(-5, 0),
),
BoxShadow(
color: Colors.white,
blurRadius: 0,
offset: Offset(5, 0),
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: const EdgeInsets.only(
left: 10,
top: 10,
bottom: 10,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
productsPromos[index].name,
style: const TextStyle(
fontSize: 20,
),
),
const SizedBox(height: 10),
const Text(
'Offer',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 5),
Text(
'${productsPromos[index].promo!.percentagePromo}% Off',
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
),
]),
const Icon(
Icons.keyboard_arrow_right_outlined,
size: 40,
),
],
),
),
),
),
],
),
);
}),
);
},
),
),
BlocBuilder<ProductsBloc, ProductsState>(
builder: (context, state) {
final List<Product> productsPromos = state.products
.where((product) => product.hasPromo == true)
.toList();
return DotsIndicator(
dotsCount: productsPromos.length,
position: _currPageValue,
decorator: DotsDecorator(
activeSize: Size(Dimensions.width20, Dimensions.height10),
activeShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
5,
),
),
),
);
},
),
SizedBox(height: Dimensions.height5),
const NavigationBarTab(),
],
),
);
}
}
- 下面的NavigationBarTab()頁面:它是一個帶有Tab Item Menu的 Column ,后跟相應的tabView page。每個 tabView 頁面都是一個 ListViewBuilder FoodListView()
const NavigationBarTab({Key? key}) : super(key: key);
@override
State<NavigationBarTab> createState() => _NavigationBarTabState();
}
class _NavigationBarTabState extends State<NavigationBarTab>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final TabController tabController = TabController(
length: 4,
vsync: this,
);
return Column(
children: [
SizedBox(
height: 30,
child: TabBar(
isScrollable: false,
controller: tabController,
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
tabs: const [
Tab(
text: 'Pizza',
),
Tab(
text: 'Specials',
),
Tab(
text: 'Desserts',
),
Tab(
text: 'Drinks',
),
],
),
),
SizedBox(
height: 700,
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: tabController,
children: const [
Expanded(child: FoodListView()),
Expanded(child: FoodListView()),
Expanded(child: FoodListView()),
Expanded(child: FoodListView()),
],
),
)
],
);
}
}
- 最后是FoodListView()頁面:具有 NeverScrollable 物理特性和將 shrinkWrap 設定為 true 的 ListViewBuilder。
class FoodListView extends StatefulWidget {
const FoodListView({Key? key}) : super(key: key);
@override
State<FoodListView> createState() => _FoodListViewState();
}
class _FoodListViewState extends State<FoodListView> {
final PageController _pageController = PageController();
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<ProductsBloc, ProductsState>(
builder: (context, state) {
final List<Product> products = state.products
.where(
(element) => element.hasPromo == false,
)
.toList();
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: products.length,
itemBuilder: ((context, index) {
return FoodCard(
index: index,
products: products,
);
}),
);
},
);
}
}
- FoodCard ()是一個具有固定高度的容器。
final int index;
final List<Product> products;
const FoodCard({
Key? key,
required this.index,
required this.products,
}) : super(key: key);
@override
State<FoodCard> createState() => _FoodCardState();
}
class _FoodCardState extends State<FoodCard> {
@override
Widget build(BuildContext context) {
return BlocBuilder<ProductsBloc, ProductsState>(
builder: (context, state) {
return Container(
height: 125,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.grey,
),
),
margin: const EdgeInsets.only(
left: 5,
right: 5,
top: 5,
),
child: Row(
children: [
Container(
margin: const EdgeInsets.only(
top: 5,
left: 5,
bottom: 5,
),
height: Dimensions.height120,
width: Dimensions.width120,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage(
widget.products[widget.index].image,
),
),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10),
),
),
),
Expanded(
child: Container(
padding: const EdgeInsets.only(
left: 5,
),
margin: const EdgeInsets.only(
top: 5,
bottom: 5,
right: 5,
),
height: Dimensions.height120,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.products[widget.index].name,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: Dimensions.height10,
),
Expanded(
child: Stack(children: [
SizedBox(
height: Dimensions.height150,
width: Dimensions.width210,
child: Text(
widget.products[widget.index].description,
maxLines: 4,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
),
),
]),
),
SizedBox(
height: Dimensions.height5,
),
Text(
'\$ ${widget.products[widget.index].price}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
),
Container(
height: Dimensions.height30,
width: Dimensions.width30,
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.shopping_cart,
color: Colors.white,
),
)
],
),
),
),
],
),
);
},
);
}
}

uj5u.com熱心網友回復:
當您嘗試在內部或小Expanded部件中間接使用兩個小部件時,您也應該將父級包裝起來,因此洗掉并執行以下操作:columnrowExpandedSizedBox
Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: tabController,
children: const [
FoodListView(),
FoodListView(),
FoodListView(),
FoodListView(),
],
),
),
uj5u.com熱心網友回復:
您可以將整個小部件移動到展開的內部。這被認為是最常用的方法。像這樣->
Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: _yourController,
children: const [
FoodListView(),
FoodListView(),
FoodListView(),
FoodListView(),
],
),
),
為此,您必須洗掉您正在使用的 ScrollView。 或者在您的方式中,您可以嘗試做的是,在您的 ListView.Builder() 中啟用 ->“shrinkWrap:true”。在這兩種方式中,您應該能夠擺脫現在提供的固定高度。
uj5u.com熱心網友回復:
我通過用IndexedStack小部件替換TabBarView解決了這個問題。它作業得很好!下面是 indexedStack 的代碼:
IndexedStack(
index: _selectedIndex,
children: [
Visibility(
visible: _selectedIndex == 0,
maintainState: true,
child: const FoodListView(
category: "Pizza",
),
),
Visibility(
visible: _selectedIndex == 1,
maintainState: true,
child: const FoodListView(
category: "Specialities",
),
),
Visibility(
visible: _selectedIndex == 2,
maintainState: true,
child: const FoodListView(
category: "Desserts",
),
),
Visibility(
visible: _selectedIndex == 3,
maintainState: true,
child: const Text("index 3"),
),
],
)```
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/509932.html
