詳解Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表
前言
今天,我將提供一個(gè)實(shí)際的示例,演示如何在單個(gè)頁(yè)面上實(shí)現(xiàn)多個(gè)列表,這些列表可以水平排列、網(wǎng)格格式、垂直排列,甚至是這些常用布局的組合。
下面是要做的:

實(shí)現(xiàn)
讓我們從創(chuàng)建一個(gè)包含產(chǎn)品所有屬性的產(chǎn)品模型開(kāi)始。
class Product {
final String id;
final String name;
final double price;
final String image;
const Product({
required this.id,
required this.name,
required this.price,
required this.image,
});
factory Product.fromJson(Map json) {
return Product(
id: json['id'],
name: json['name'],
price: json['price'],
image: json['image'],
);
}
}現(xiàn)在,我們將設(shè)計(jì)我們的小部件以支持水平、垂直和網(wǎng)格視圖。
創(chuàng)建一個(gè)名為 HorizontalRawWidget 的新窗口小部件類(lèi),定義水平列表的用戶(hù)界面。
import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';
class HorizontalRawWidget extends StatelessWidget {
final Product product;
const HorizontalRawWidget({Key? key, required this.product})
: super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
left: 15,
),
child: Container(
width: 125,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(12)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
product.image,
height: 130,
fit: BoxFit.contain,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(product.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold)),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text("\$${product.price}",
style: const TextStyle(
color: Colors.black, fontSize: 12)),
],
),
],
),
),
)
],
),
),
);
}
}設(shè)計(jì)一個(gè)名為 GridViewRawWidget 的小部件類(lèi),定義單個(gè)網(wǎng)格視圖的用戶(hù)界面。
import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';
class GridViewRawWidget extends StatelessWidget {
final Product product;
const GridViewRawWidget({Key? key, required this.product}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(10)),
child: Column(
children: [
AspectRatio(
aspectRatio: 1,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
product.image,
fit: BoxFit.fill,
),
),
)
],
),
);
}
}最后,讓我們?yōu)榇怪币晥D創(chuàng)建一個(gè)小部件類(lèi)。
import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';
class VerticalRawWidget extends StatelessWidget {
final Product product;
const VerticalRawWidget({Key? key, required this.product}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
color: Colors.white,
child: Row(
children: [
Image.network(
product.image,
width: 78,
height: 88,
),
const SizedBox(
width: 15,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(fontSize: 12, color: Colors.black, fontWeight: FontWeight.bold),
),
SizedBox(
height: 5,
),
Text("\$${product.price}",
style: const TextStyle(color: Colors.black, fontSize: 12)),
],
),
)
],
),
);
}
}現(xiàn)在是時(shí)候把所有的小部件合并到一個(gè)屏幕中了,我們先創(chuàng)建一個(gè)名為“home_page.dart”的頁(yè)面,在這個(gè)頁(yè)面中,我們將使用一個(gè)橫向的 ListView、縱向的 ListView 和 GridView。
import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';
import 'package:multiple_listview_example/utils/product_helper.dart';
import 'package:multiple_listview_example/views/widgets/gridview_raw_widget.dart';
import 'package:multiple_listview_example/views/widgets/horizontal_raw_widget.dart';
import 'package:multiple_listview_example/views/widgets/title_widget.dart';
import 'package:multiple_listview_example/views/widgets/vertical_raw_widget.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
List products = ProductHelper.getProductList();
return Scaffold(
backgroundColor: const Color(0xFFF6F5FA),
appBar: AppBar(
centerTitle: true,
title: const Text("Home"),
),
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TitleWidget(title: "Horizontal List"),
const SizedBox(
height: 10,
),
SizedBox(
height: 200,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: products.length,
itemBuilder: (BuildContext context, int index) {
return HorizontalRawWidget(
product: products[index],
);
}),
),
const SizedBox(
height: 10,
),
const TitleWidget(title: "Grid View"),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 13,
mainAxisSpacing: 13,
childAspectRatio: 1),
itemCount: products.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return GridViewRawWidget(
product: products[index],
);
}),
),
const TitleWidget(title: "Vertical List"),
ListView.builder(
itemCount: products.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return VerticalRawWidget(
product: products[index],
);
}),
],
),
),
),
);
}
}我使用了一個(gè) SingleChildScrollView widget 作為代碼中的頂部根 widget,考慮到我整合了多個(gè)布局,如水平列表、網(wǎng)格視圖和垂直列表,我將所有這些 widget 包裝在一個(gè) Column widget 中。
挑戰(zhàn)在于如何處理多個(gè)滾動(dòng)部件,因?yàn)樵谏鲜鍪纠杏袃蓚€(gè)垂直滾動(dòng)部件:一個(gè)網(wǎng)格視圖和一個(gè)垂直列表視圖。為了禁用單個(gè)部件的滾動(dòng)行為, physics 屬性被設(shè)置為 const NeverScrollableScrollPhysics()。取而代之的是,使用頂層根 SingleChildScrollView` 來(lái)啟用整個(gè)內(nèi)容的滾動(dòng)。此外, SingleChildScrollView上的shrinkWrap屬性被設(shè)置為true,以確保它能緊緊包裹其內(nèi)容,只占用其子控件所需的空間。
Github 鏈接:https://github.com/tarunaronno005/flutter-multiple-listview
到此這篇關(guān)于詳解Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表的文章就介紹到這了,更多相關(guān)Flutter單個(gè)屏幕實(shí)現(xiàn)多個(gè)列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)點(diǎn)擊WebView界面中圖片滑動(dòng)瀏覽與保存圖片功能
大家在日常使用spp流量文章的時(shí)候經(jīng)常會(huì)遇到這樣的一個(gè)功能,點(diǎn)擊文章的圖片進(jìn)入圖片的瀏覽模式,可以左右滑動(dòng)圖片瀏覽,并且可以實(shí)現(xiàn)保存圖片的功能,所以本文主要就介紹了在Android如何實(shí)現(xiàn)點(diǎn)擊WebView界面中圖片滑動(dòng)瀏覽與保存圖片功能,需要的朋友可以參考下。2017-04-04
android中view手勢(shì)滑動(dòng)沖突的解決方法
本篇文章主要介紹了android中view手勢(shì)滑動(dòng)沖突的解決方法,主要解決方法有兩種,外部和內(nèi)部攔截。有需要的可以參考下。2016-11-11
Android編程中activity的完整生命周期實(shí)例詳解
這篇文章主要介紹了Android編程中activity的完整生命周期,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android編程中activity的原理與具體用法,需要的朋友可以參考下2015-12-12
Android DataBinding手把手入門(mén)教程
2015年谷歌I/O大會(huì)上介紹了一個(gè)框架DataBinding,DataBinding是一個(gè)數(shù)據(jù)綁定框架,以前我們?cè)贏ctivity里寫(xiě)很多的findViewById,現(xiàn)在如果我們使用DataBinding,就可以拋棄findViewById2021-10-10
Android編程開(kāi)發(fā)之seekBar采用handler消息處理操作的方法
這篇文章主要介紹了Android編程開(kāi)發(fā)之seekBar采用handler消息處理操作的方法,結(jié)合實(shí)例分析了Android實(shí)現(xiàn)進(jìn)度條功能的相關(guān)技巧,需要的朋友可以參考下2015-12-12
Android指紋識(shí)別認(rèn)識(shí)和基本使用詳解
這篇文章主要為大家詳細(xì)介紹了Android指紋識(shí)別認(rèn)識(shí)和基本的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android廣播接實(shí)現(xiàn)監(jiān)聽(tīng)電話狀態(tài)(電話的狀態(tài),攔截)
這篇文章主要介紹了Android廣播接實(shí)現(xiàn)監(jiān)聽(tīng)電話狀態(tài)(電話的狀態(tài),攔截) 的相關(guān)資料,需要的朋友可以參考下2016-03-03
Android插件化-RePlugin項(xiàng)目集成與使用詳解
這篇文章主要介紹了Android插件化-RePlugin項(xiàng)目集成與使用詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11

