在 Flutter 中建立圖像輪播
從社交媒體應用程式到電子商務應用程式,大多數現代應用程式都有某種圖像輪播來展示産品、圖像或廣告。 由于 flutter 提供的内置小部件,從頭開始實作圖像輪播并不像您想象的那麼難。 在本文中,您将學習如何從頭開始建立圖像輪播并根據需要進行自定義。最後,您将學習如何使用carousel_slider插件以更少的代碼建立圖像輪播。 這些是我們将要讨論的主題。
- 圖像輪播的小部件結構
- 使用 PageView 小部件建立輪播
- 控制初始頁面
- 添加位置訓示器
- 為滑動圖像添加動畫
- 如何使用 carousel_slider 插件
圖像輪播的小部件結構
在我們進入編碼部分之前,讓我們了解我們需要擁有的小部件結構。
頁面視圖小部件需要在應用程式中實作圖像滑動功能和圖像視圖以顯示實際圖像。除此之外,您需要一個容器小部件來在滑塊底部實作頁面訓示器。
使用 PageView 小部件建立輪播
首先,讓我們建立一個名為 Carousel 的有狀态小部件。這是我們将用來實作輪播的小部件。 首先,讓我們建立一個包含圖像 URL 的清單:
List images = [
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQTIZccfNPnqalhrWev-Xo7uBhkor57_rKbkw&usqp=CAU",
"https://wallpaperaccess.com/full/2637581.jpg"
];
現在您可以使用 PageView 小部件建構器方法來建立輪播頁面:
PageView.builder(
itemCount: 2,
pageSnapping: true,
itemBuilder: (context,pagePosition){
return Container(
margin: EdgeInsets.all(10),
child: Image.network());
})
itemCount
代表頁數,它會決定
itemBuilder
需要執行多少次。是以,您可以通過通路它的索引來設定每個圖像 URL。
PageView.builder(
itemCount: 2,
pageSnapping: true,
itemBuilder: (context,pagePosition){
return Container(
margin: EdgeInsets.all(10),
child: Image.network(images[pagePosition]));
})
現在您可以在螢幕上看到這些圖像,并且可以滑動這些圖像。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiYWan5iN5IjN2YWO1QDZ5EWY0MTYxYzXzUzMycTM4IzLcFTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.gif)
如果您像這樣顯示圖像,使用者将不知道是否有更多圖像。是以,在顯示中間圖像的同時顯示左右圖像的一部分将改善圖像輪播的使用者體驗。 首先,您應該建立
PageController
并将其設定為我們的 PageView 小部件。此外,我添加了第三張圖像并更改了
itemCount
從圖像數組本身的長度擷取計數。然後您可以設定該
viewportFraction
屬性
PageController
以顯示其他圖像的分數:
late PageController _pageController;
List images = [
"https://images.wallpapersden.com/image/download/purple-sunrise-4k-vaporwave_bGplZmiUmZqaraWkpJRmbmdlrWZlbWU.jpg",
"https://wallpaperaccess.com/full/2637581.jpg",
"https://uhdwallpapers.org/uploads/converted/20/01/14/the-mandalorian-5k-1920x1080_477555-mm-90.jpg"
];
@override
void initState() {
super.initState();
_pageController = PageController(viewportFraction: 0.8);
}
@override
Widget build(BuildContext context) {
return PageView.builder(
itemCount: images.length,
pageSnapping: true,
controller: _pageController,
onPageChanged: (page) {
setState(() {
activePage = page;
});
},
itemBuilder: (context, pagePosition) {
return Container(
margin: EdgeInsets.all(10),
child: Image.network(images[pagePosition]),
);
});
}
盡管圖像顯示在 PageView 的中間,但它實際上占用了整個螢幕空間。如果您将
fit
類型更改為
cover
,您可以看到它占據了整個螢幕。
要控制它,您可以将小部件包裝在小
SizedBox
部件中:
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width,
child: PageView.builder(
itemCount: images.length,
pageSnapping: true,
controller: _pageController,
onPageChanged: (page) {
setState(() {
activePage = page;
});
},
itemBuilder: (context, pagePosition) {
return Container(
margin: EdgeInsets.all(10),
child: Image.network(images[pagePosition],fit: BoxFit.cover,),
);
}),
)
控制初始頁面
輪播在第一次加載時将第一張圖檔加載為預設頁面。但是如果您需要更改初始圖像以從不同的頁面開始,您可以
initialPage
在 PageController 中提供該屬性。它接受索引作為一個位置:
_pageController = PageController(viewportFraction: 0.8,initialPage: 1);
添加位置訓示器
首先,将
PageView
小部件移動到小部件
SizedBox
内部
Column
:
Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
height: 200,
child: PageView.builder(
itemCount: images.length,
pageSnapping: true,
controller: _pageController,
onPageChanged: (page) {
setState(() {
activePage = page;
});
},
itemBuilder: (context, pagePosition) {
return Container(
margin: EdgeInsets.all(10),
child: Image.network(images[pagePosition]),
);
}),
),
],
)
然後您可以建立一個方法來傳回名額清單,您應該根據目前活動位置更改名額的顔色。是以,讓我們建立一個接受
currentIndex
和
imagesLength
作為參數的方法。通過檢查索引,您可以更改活動位置訓示器的顔色,如下所示:
List indicators(imagesLength,currentIndex) {
return List.generate(imagesLength, (index) {
return Container(
margin: EdgeInsets.all(3),
width: 10,
height: 10,
decoration: BoxDecoration(
color: currentIndex == index ? Colors.black : Colors.black26,
shape: BoxShape.circle),
);
});
}
當使用者滑動圖像時,應該有一種方法可以擷取目前活動位置。該
onPageChanged
方法适用于:
int activePage = 1;
PageView.builder(
itemCount: images.length,
pageSnapping: true,
controller: _pageController,
onPageChanged: (page) {
setState(() {
activePage = page;
});
})
現在您可以将
activePage
值傳遞給我們建立的方法。在小部件内建立一個 Row 小
Column
部件作為第二個孩子。然後,您可以将方法的傳回訓示符
indicators
作為子項配置設定給
Row
小部件:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(images.length,activePage))
為滑動圖像添加動畫
盡管我們的輪播效果很好,但在圖像之間滑動時有動畫效果還是不錯的。讓我們看看如何在圖像變化時添加動畫。 有多種方法可用于應用放大動畫。但是在這裡我們将使用内置的 Flutter
AnimationContainer
小部件來建立這個動畫,因為它開箱即用,
AnimationContainer
提供了我們需要的所有功能。 在目前滑塊圖像中,所有容器的大小都相同。通過更改圖像的邊距,您可以添加放大效果。
AnimationContainer
包含樣特性
duration
,
curve
,
margin
,和
decoration
你可以用它來實作這個動畫。 讓我們建立一個單獨的滑塊方法,并将圖像清單、目前頁面位置以及目前圖像是否處于活動狀态作為參數。可以
itemBuilder
通過檢查頁面位置和目前活動頁面位置來檢查此活動狀态。 該
curve
屬性可用于指定需要如何放置動畫曲線。請檢視這篇Flutter 文章以了解可用值及其行為:
itemBuilder: (context, pagePosition) {
//checking active position
bool active = pagePosition == activePage;
return slider(images,pagePosition,active);
}
AnimatedContainer slider(images,pagePosition,active){
double margin = active ? 10 : 20;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic,
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
image: DecorationImage(image: NetworkImage(images[pagePosition]))
),
);
}
如何使用 carousel_slider 插件
盡管從頭開始實施輪播很容易,但根據您的項目和時間表,添加不同的功能可能很困難。是以,可以在您的項目中使用像 carousel_slider 這樣的插件來建立一個更輕松的輪播。 首先,将插件添加到:
pubspec.yaml
dependencies:
carousel_slider: ^4.0.0
接下來,導入您要使用滑塊的庫:
import 'package:carousel_slider/carousel_slider.dart';
該
CarouselSlider
類主要接受兩個參數:
options
和
items
。對于選項,您可以配置滑塊所需的行為。我已經提到了
CarouselOptions
類中可用的一些屬性。
Property | 描述 |
| 設定滑塊高度 |
| 設定頁面是否需要無限滾動。預設情況下,此選項處于打開狀态,您可以根據需要将其關閉 |
| 滑塊将自動更改圖像。預設時間為 4 秒 |
| 更改自動播放間隔,預設4秒 |
| 頁面更改時的回調觸發器。它包含兩個參數:目前位置和原因。該 值可以是定時、手動或控制器 |
| 放大中間頁面,就像我們之前實作的那樣 |
SliderPlugin(images) {
return CarouselSlider(
options: CarouselOptions(
height: 200.0,
enlargeCenterPage: true,
onPageChanged: (position,reason){
print(reason);
print(CarouselPageChangedReason.controller);
},
enableInfiniteScroll: false,
),
items: images.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
image: DecorationImage(image: NetworkImage(i))));
},
);
}).toList(),
);
}
結論
最後附上完整源碼
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Image carousel'),
),
body: Carousel(),
),
);
}
}
class Carousel extends StatefulWidget {
const Carousel({
Key? key,
}) : super(key: key);
@override
State createState() => _CarouselState();
}
class _CarouselState extends State {
late PageController _pageController;
List images = [
"https://images.wallpapersden.com/image/download/purple-sunrise-4k-vaporwave_bGplZmiUmZqaraWkpJRmbmdlrWZlbWU.jpg",
"https://wallpaperaccess.com/full/2637581.jpg",
"https://uhdwallpapers.org/uploads/converted/20/01/14/the-mandalorian-5k-1920x1080_477555-mm-90.jpg"
];
int activePage = 1;
@override
void initState() {
super.initState();
_pageController = PageController(viewportFraction: 0.8, initialPage: 1);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
height: 200,
child: PageView.builder(
itemCount: images.length,
pageSnapping: true,
controller: _pageController,
onPageChanged: (page) {
setState(() {
activePage = page;
});
},
itemBuilder: (context, pagePosition) {
bool active = pagePosition == activePage;
return slider(images,pagePosition,active);
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(images.length,activePage))
],
);
}
}
AnimatedContainer slider(images, pagePosition, active) {
double margin = active ? 10 : 20;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic,
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
image: DecorationImage(image: NetworkImage(images[pagePosition]))),
);
}
imageAnimation(PageController animation, images, pagePosition) {
return AnimatedBuilder(
animation: animation,
builder: (context, widget) {
print(pagePosition);
return SizedBox(
width: 200,
height: 200,
child: widget,
);
},
child: Container(
margin: EdgeInsets.all(10),
child: Image.network(images[pagePosition]),
),
);
}
List indicators(imagesLength, currentIndex) {
return List.generate(imagesLength, (index) {
return Container(
margin: EdgeInsets.all(3),
width: 10,
height: 10,
decoration: BoxDecoration(
color: currentIndex == index ? Colors.black : Colors.black26,
shape: BoxShape.circle),
);
});
}