Flutter多方向水平滚动列表插件multi_directional_horizontal_list的使用
Flutter多方向水平滚动列表插件multi_directional_horizontal_list的使用
一个多方向水平滚动的ListView
。

特性
- 简单创建类似时间轴的界面。
- 轻松实现前后通信。
- 可以通过编程方式跳转或动画到特定索引。
开始使用
在你的pubspec.yaml
文件中添加插件:
dependencies:
multi_directional_horizontal_list: ^0.0.3
在你的Dart文件中导入库:
import 'package:multi_directional_horizontal_list/multi_directional_horizontal_list.dart';
创建一个MultiDirectionalHorizontalList
小部件:
final MultiDirectionalHorizontalListController controller = MultiDirectionalHorizontalListController();
MultiDirectionalHorizontalList(
controller: controller,
itemCount: 20,
onLeftLoaded: () {
print("Reached left end");
},
onRightLoaded: () {
print("Reached right end");
},
itemBuilder: (context, index) {
return SizedBox(); // 这里可以替换为你的自定义组件
}
);
如果你使用了MultiDirectionalHorizontalListController
,你可以通过编程方式滚动到特定位置:
通过索引滚动到元素的位置:
controller.animateTo(4); // 平滑滚动到索引4
controller.jumpTo(4); // 立即跳转到索引4
你也可以附加监听器来监听滚动事件,例如方向或位置:
controller.addListener((event) {
print('Current Position: ${event.position}');
});
完整示例
以下是一个完整的示例,展示了如何使用multi_directional_horizontal_list
插件创建一个日期选择器。
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:multi_directional_horizontal_list/multi_directional_horizontal_list.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: '多方向水平滚动列表演示',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pinkAccent),
useMaterial3: true,
),
home: const MyHomePage(title: ''),
);
}
}
class MyHomePage extends StatefulWidget {
final MultiDirectionalHorizontalListController? testingController;
const MyHomePage({
super.key,
required this.title,
this.testingController,
});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late MultiDirectionalHorizontalListController controller;
DateTime _selectedDateStart = DateTime.now();
int _activeIndex = 0;
final double itemWidth = 85;
late double middle = -(MediaQuery.sizeOf(context).width) / 2 + itemWidth / 2;
[@override](/user/override)
initState() {
controller =
widget.testingController ?? MultiDirectionalHorizontalListController()
..addListener((event) {
_handleCallbackEvent(event);
});
super.initState();
}
void _handleCallbackEvent(ScrollEvent? event) {
event?.randomCallback?.call();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 月和年
Text(
_selectedDateStart.monthAndDay,
key: const ValueKey(0),
maxLines: 1,
textAlign: TextAlign.center,
style: GoogleFonts.lato(
textStyle: Theme.of(context).textTheme.headlineMedium,
),
),
MultiDirectionalHorizontalList.builder(
controller: controller,
initialScrollOffset: middle,
itemCount: 100,
height: 60,
onLeftLoaded: () {},
onRightLoaded: () {},
itemBuilder: (context, index) {
// 当前日期时间
DateTime currentDateTime = DateTime(
DateTime.now().year,
DateTime.now().month,
DateTime.now().day + index,
);
bool isSelected = currentDateTime.isSelected(_selectedDateStart);
bool isToday = currentDateTime.isToday;
return InkWell(
splashFactory: NoSplash.splashFactory,
highlightColor: Colors.transparent,
onTap: () {
setState(() {
_selectedDateStart = currentDateTime;
_activeIndex = index;
});
},
child: SizedBox(
width: itemWidth,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
NotificationListener(
onNotification:
(SizeChangedLayoutNotification notification) {
controller.animateTo(
middle + (_activeIndex - 1) * itemWidth + itemWidth,
);
return true;
},
child: SizeChangedLayoutNotifier(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: isSelected
? Stack(
children: [
Positioned(
top: 0,
right: 0,
child: isToday
? const RedDot()
: const SizedBox.shrink(),
),
Text(
currentDateTime.getDay(
abbreviate: false),
key: const ValueKey(0),
maxLines: 1,
textAlign: TextAlign.center,
style: GoogleFonts.lato(
textStyle: Theme.of(context)
.textTheme
.titleMedium,
),
),
],
)
: Stack(
children: [
Positioned(
top: 0,
right: 0,
child: isToday
? const RedDot()
: const SizedBox.shrink(),
),
Text(
currentDateTime.getDay(
abbreviate: true),
key: const ValueKey(1),
maxLines: 1,
textAlign: TextAlign.center,
style: GoogleFonts.lato(
textStyle: Theme.of(context)
.textTheme
.titleSmall
?.copyWith(
color: Colors.black54,
),
),
),
],
),
),
),
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: isSelected
? Text(
currentDateTime.dayInNo,
key: const ValueKey(0),
style: GoogleFonts.lato(
textStyle: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.black),
),
)
: Text(
currentDateTime.dayInNo,
key: const ValueKey(1),
style: GoogleFonts.lato(
textStyle: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(color: Colors.black54),
),
),
),
const SizedBox(
height: 8,
),
AnimatedScale(
duration: const Duration(milliseconds: 600),
scale: isSelected ? 1 : 0,
curve: isSelected
? Curves.decelerate
: Curves.easeOutQuart,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(40),
topLeft: Radius.circular(40),
),
color: Colors.black54,
),
width: itemWidth,
height: 4,
),
)
],
),
),
);
},
),
],
),
);
}
}
extension DateUtils on DateTime {
bool get isToday {
final now = DateTime.now();
return now.day == day && now.month == month && now.year == year;
}
bool get isTomorrow {
final tomorrow = DateTime.now().add(const Duration(days: 1));
return tomorrow.day == day &&
tomorrow.month == month &&
tomorrow.year == year;
}
bool get isYesterday {
final yesterday = DateTime.now().subtract(const Duration(days: 1));
return yesterday.day == day &&
yesterday.month == month &&
yesterday.year == year;
}
bool isSelected(DateTime selected) {
return selected.day == day &&
selected.month == month &&
selected.year == year;
}
String get monthAndDay {
return DateFormat.yMMMM().format(this);
}
String get dayInNo {
return DateFormat.d().format(this);
}
String getMonth({bool includeYear = false}) {
if (includeYear) {
return DateFormat.yMMMM().format(this);
}
return DateFormat.MMMM().format(this);
}
String getDay({bool abbreviate = false, bool alphabet = false}) {
if (alphabet) {
return DateFormat.EEEEE().format(this);
}
if (abbreviate) {
return DateFormat.E().format(this);
}
return DateFormat.EEEE().format(this);
}
}
class RedDot extends StatelessWidget {
const RedDot({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return FractionalTranslation(
translation: const Offset(1.0, -1.0),
child: Container(
height: 5,
width: 5,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.redAccent,
),
),
);
}
}
更多关于Flutter多方向水平滚动列表插件multi_directional_horizontal_list的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter多方向水平滚动列表插件multi_directional_horizontal_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter中使用multi_directional_horizontal_list
插件的一个示例代码案例。这个插件允许你创建一个多方向水平滚动的列表。首先,你需要确保已经在pubspec.yaml
文件中添加了该插件的依赖:
dependencies:
flutter:
sdk: flutter
multi_directional_horizontal_list: ^最新版本号 # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,下面是一个使用multi_directional_horizontal_list
的示例代码:
import 'package:flutter/material.dart';
import 'package:multi_directional_horizontal_list/multi_directional_horizontal_list.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Multi-Directional Horizontal List Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiDirectionalHorizontalListDemo(),
);
}
}
class MultiDirectionalHorizontalListDemo extends StatefulWidget {
@override
_MultiDirectionalHorizontalListDemoState createState() => _MultiDirectionalHorizontalListDemoState();
}
class _MultiDirectionalHorizontalListDemoState extends State<MultiDirectionalHorizontalListDemo> {
final List<String> items = List.generate(50, (index) => "Item $index");
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Multi-Directional Horizontal List Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: MultiDirectionalHorizontalList(
// 初始化水平方向滚动列表
itemCount: items.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.withOpacity(0.5)),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
items[index],
style: TextStyle(fontSize: 18),
),
),
);
},
// 配置滚动方向
scrollDirections: [
Axis.horizontal, // 水平方向
// 你可以添加更多方向,例如 Axis.vertical,但这通常不是此插件的主要用途
],
// 其他可选配置
scrollPhysics: BouncingScrollPhysics(),
controller: ScrollController(),
),
),
);
}
}
在这个示例中,我们做了以下几件事:
- 添加依赖:在
pubspec.yaml
文件中添加了multi_directional_horizontal_list
依赖。 - 创建主应用:使用
MaterialApp
创建了一个简单的Flutter应用。 - 创建演示页面:定义了一个
MultiDirectionalHorizontalListDemo
页面,该页面包含一个MultiDirectionalHorizontalList
组件。 - 配置列表:在
MultiDirectionalHorizontalList
中,我们配置了itemCount
和itemBuilder
来生成列表项,并指定了滚动方向为Axis.horizontal
。
请注意,multi_directional_horizontal_list
插件的具体API和使用方式可能会随着版本更新而变化,因此请参考最新的官方文档和示例代码。如果插件支持更多复杂的滚动方向和配置,你可以进一步探索其API文档。