Flutter日历日视图插件calendar_day_view的使用
Flutter日历日视图插件calendar_day_view的使用
calendar_day_view
是一个功能强大且高度可定制的日历日视图库,它专注于提供良好的日视图体验。尽管有很多日历包可供选择,但它们并不都能很好地支持日视图。calendar_day_view
并不是要取代其他日历组件,而是为了补充现有组件,使其在应用中表现得更好。
主要特性
- 类别溢出日视图(Category Overflow Day View):将一天分为多个类别,并且事件可以在同一类别内跨越不同的时间槽显示。
- 类别日视图(Category Day View):展示一天内带有多个类别的事件。
- 溢出日视图(Over flow Day View):类似传统日历,事件根据其持续时间扩展显示在多个时间行上。
- 行内日视图(In Row Day View):在同一时间窗口内显示所有事件。
- 事件日视图(Event Day View):仅展示当天的所有事件及其开始时间。
- 支持自定义一天的开始和结束时间、时间间隔(单行代表的时间长度)、是否显示当前时间线等。
- 用户可以点击日视图以执行特定操作(例如,在指定时间创建事件)。
安装与导入库
在 pubspec.yaml
文件中添加依赖项:
dependencies:
calendar_day_view: ^latest_version
然后在需要使用的文件中导入:
import 'package:calendar_day_view/calendar_day_view.dart';
使用示例
以下是一个完整的示例应用程序,演示了如何使用 calendar_day_view
插件中的不同类型的日视图。
示例代码
import 'dart:math';
import 'package:calendar_day_view/calendar_day_view.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:faker/faker.dart';
final timeFormat = DateFormat('ha');
final rd = Random();
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.white)),
home: const CalendarDayViewExample(),
);
}
}
class CalendarDayViewExample extends StatefulWidget {
const CalendarDayViewExample({super.key});
@override
State<CalendarDayViewExample> createState() => _CalendarDayViewExampleState();
}
class _CalendarDayViewExampleState extends State<CalendarDayViewExample> {
List<DayEvent<String>> dayEvents = fakeEvents().map((e) => e.copyWith(end: e.start.add(Duration(minutes: faker.randomGenerator.element([20, 30, 90, 60]))))).toList();
List<EventCategory> categories = [
EventCategory(id: "1", name: "cate 1"),
EventCategory(id: "2", name: "cate 2"),
EventCategory(id: "3", name: "cate 3"),
EventCategory(id: "4", name: "cate 4"),
];
List<CategorizedDayEvent<String>> categoryEvents = genEvents(categories.length);
void addCategory() {
setState(() {
categories.add(EventCategory(id: "${categories.length + 1}", name: "cate ${categories.length + 1}"));
categoryEvents = genEvents(categories.length);
});
}
int currentIndex = 0;
@override
Widget build(BuildContext context) {
final bodyItems = [
// OverflowDayViewTab 实现
OverflowDayViewTab(
events: dayEvents,
onTimeTap: (time) {
setState(() {
dayEvents = [
...dayEvents,
DayEvent(
value: faker.conference.name(),
start: time,
end: time.add(Duration(minutes: faker.randomGenerator.element([20, 140]))),
)
];
});
},
),
// CategoryOverflowDayViewTab 实现
CategoryOverflowDayViewTab(
events: categoryEvents,
categories: categories,
addEventOnClick: (cate, time) {
setState(() {
categoryEvents = [
...categoryEvents,
CategorizedDayEvent(
categoryId: cate.id,
value: faker.conference.name(),
start: time)
];
});
},
),
// CategoryDayViewTab 实现
CategoryDayViewTab(
events: categoryEvents,
categories: categories,
addEventOnClick: (cate, time) {
setState(() {
categoryEvents = [
...categoryEvents,
CategorizedDayEvent(
categoryId: cate.id,
value: faker.conference.name(),
start: time)
];
});
},
),
// InRowDayViewTab 实现
InRowDayViewTab(
events: dayEvents,
),
// EventDayViewTab 实现
EventDayViewTab(events: dayEvents),
];
return DefaultTabController(
length: 5,
child: SafeArea(
child: Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Theme.of(context).colorScheme.background,
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedFontSize: 14,
unselectedItemColor: Colors.black,
selectedItemColor: Colors.blue,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.calendar_month), label: "Overflow"),
BottomNavigationBarItem(icon: Icon(Icons.calendar_view_day), label: "Category Overflow"),
BottomNavigationBarItem(icon: Icon(Icons.calendar_view_day), label: "Category"),
BottomNavigationBarItem(icon: Icon(Icons.calendar_today_outlined), label: "In Row"),
BottomNavigationBarItem(icon: Icon(Icons.calendar_view_month), label: "Events"),
],
onTap: (value) => setState(() => currentIndex = value),
currentIndex: currentIndex,
),
appBar: AppBar(
title: Text(
"${getTitle(currentIndex)} - ${currentIndex == 1 || currentIndex == 2 ? categoryEvents.length : dayEvents.length} events",
style: const TextStyle(color: Colors.teal, fontSize: 30),
),
toolbarHeight: 100,
centerTitle: false,
actions: [
Row(
children: [
if (currentIndex == 1 || currentIndex == 2)
Row(
children: [
TextButton.icon(
style: TextButton.styleFrom(backgroundColor: Colors.white),
onPressed: () => setState(() => categoryEvents = genEvents(categories.length)),
icon: const Icon(Icons.refresh),
label: const Text("events"),
),
const SizedBox(width: 10),
TextButton.icon(
style: TextButton.styleFrom(backgroundColor: Colors.white),
onPressed: addCategory,
icon: const Icon(Icons.add),
label: const Text("category"),
),
],
),
else
TextButton.icon(
style: TextButton.styleFrom(backgroundColor: Colors.white),
onPressed: () => setState(() => dayEvents = fakeEvents()),
icon: const Icon(Icons.refresh),
label: const Text("events"),
),
const SizedBox(width: 10),
],
)
],
),
body: bodyItems[currentIndex],
),
),
);
}
}
String getTitle(int index) {
switch (index) {
case 0:
return "Overflow Day View";
case 1:
return "Category Overflow Day View";
case 2:
return "Category Day View";
case 3:
return "In Row Day View";
case 4:
return "Events Day View";
default:
return "Calendar Day View";
}
}
List<DayEvent<String>> fakeEvents() => faker.randomGenerator.amount((i) {
final start = DateTime.now().copyWith(
hour: faker.randomGenerator.integer(19, min: 5),
minute: faker.randomGenerator.element([0, 10, 20, 40]),
second: 0,
);
return DayEvent(
value: faker.conference.name(),
start: start,
end: start.add(Duration(minutes: faker.randomGenerator.element([20, 30, 90, 60]))),
);
}, 30, min: 10);
List<CategorizedDayEvent<String>> genEvents(int categoryLength) => faker.randomGenerator.amount(
(i) {
final hour = faker.randomGenerator.integer(17, min: 7);
final start = DateTime.now().copyWith(
hour: hour,
minute: faker.randomGenerator.element([0, 15, 20]),
second: 0,
);
return CategorizedDayEvent(
categoryId: faker.randomGenerator.integer(categoryLength + 1, min: 1).toString(),
value: faker.conference.name(),
start: start,
end: start.add(Duration(minutes: faker.randomGenerator.element([90, 60])));
},
categoryLength * 5,
min: 10,
);
// 溢出日视图标签页
class OverflowDayViewTab extends StatelessWidget {
final List<DayEvent<String>> events;
final Function(TimeOfDay) onTimeTap;
const OverflowDayViewTab({required this.events, required this.onTimeTap, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CalendarDayView.overflow(
config: OverFlowDayViewConfig(
currentDate: DateTime.now(),
timeGap: 60,
heightPerMin: 2,
endOfDay: const TimeOfDay(hour: 20, minute: 0),
startOfDay: const TimeOfDay(hour: 4, minute: 0),
renderRowAsListView: true,
time12: true,
),
onTimeTap: (t) {
onTimeTap(t);
},
events: UnmodifiableListView(events),
overflowItemBuilder: (context, constraints, itemIndex, event) {
return Container(
color: getRandomColor(),
padding: const EdgeInsets.all(8.0),
child: Text(event.value),
);
},
);
}
}
// 类别溢出日视图标签页
class CategoryOverflowDayViewTab extends StatelessWidget {
final List<CategorizedDayEvent<String>> events;
final List<EventCategory> categories;
final Function(EventCategory, TimeOfDay) addEventOnClick;
const CategoryOverflowDayViewTab({
required this.events,
required this.categories,
required this.addEventOnClick,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CalendarDayView.category(
config: CategoryDavViewConfig(
time12: true,
allowHorizontalScroll: true,
columnsPerPage: 2,
currentDate: DateTime.now(),
timeGap: 60,
heightPerMin: 1,
evenRowColor: Colors.white,
oddRowColor: Colors.grey[200],
headerDecoration: BoxDecoration(color: Colors.lightBlueAccent.withOpacity(.5)),
logo: const Padding(
padding: EdgeInsets.all(8.0),
child: CircleAvatar(child: Text("C")),
),
),
categories: categories,
events: events,
onTileTap: (category, time) {
addEventOnClick(category, time);
},
controlBarBuilder: (goToPreviousTab, goToNextTab) => Container(), // 自定义控制栏构建器
eventBuilder: (constraints, category, _, event) {
return Container(
color: getRandomColor(),
padding: const EdgeInsets.all(8.0),
child: Text(event.value),
);
},
);
}
}
// 类别日视图标签页
class CategoryDayViewTab extends StatelessWidget {
final List<CategorizedDayEvent<String>> events;
final List<EventCategory> categories;
final Function(EventCategory, TimeOfDay) addEventOnClick;
const CategoryDayViewTab({
required this.events,
required this.categories,
required this.addEventOnClick,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CalendarDayView.category(
config: CategoryDavViewConfig(
time12: true,
allowHorizontalScroll: true,
columnsPerPage: 2,
currentDate: DateTime.now(),
timeGap: 60,
heightPerMin: 1,
evenRowColor: Colors.white,
oddRowColor: Colors.grey[200],
headerDecoration: BoxDecoration(color: Colors.lightBlueAccent.withOpacity(.5)),
logo: const Padding(
padding: EdgeInsets.all(8.0),
child: CircleAvatar(child: Text("C")),
),
),
categories: categories,
events: events,
onTileTap: (category, time) {
addEventOnClick(category, time);
},
controlBarBuilder: (goToPreviousTab, goToNextTab) => Container(), // 自定义控制栏构建器
eventBuilder: (constraints, category, _, event) {
return Container(
color: getRandomColor(),
padding: const EdgeInsets.all(8.0),
child: Text(event.value),
);
},
);
}
}
// 行内日视图标签页
class InRowDayViewTab extends StatelessWidget {
final List<DayEvent<String>> events;
const InRowDayViewTab({required this.events, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CalendarDayView<String>.inRow(
config: InRowDayViewConfig(
heightPerMin: 1,
showCurrentTimeLine: true,
dividerColor: Colors.black,
timeGap: 60,
showWithEventOnly: false,
currentDate: DateTime.now(),
startOfDay: TimeOfDay(hour: 3, minute: 00),
endOfDay: TimeOfDay(hour: 22, minute: 00),
),
events: UnmodifiableListView(events),
itemBuilder: (context, constraints, event) => Flexible(
child: Container(
color: getRandomColor(),
padding: const EdgeInsets.all(8.0),
child: Text(event.value),
),
),
);
}
}
// 事件日视图标签页
class EventDayViewTab extends StatelessWidget {
final List<DayEvent<String>> events;
const EventDayViewTab({required this.events, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CalendarDayView.eventOnly(
config: EventDayViewConfig(
showHourly: true,
currentDate: DateTime.now(),
),
events: events,
eventDayViewItemBuilder: (context, index, event) {
return Container(
color: getRandomColor(),
height: 50,
child: Text(event.value),
);
},
);
}
}
Color getRandomColor() {
return Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
}
以上代码展示了如何使用 calendar_day_view
插件的不同视图类型,并提供了交互功能,如添加事件、刷新数据等。你可以根据自己的需求调整配置参数和样式。希望这个示例能帮助你更好地理解和使用 calendar_day_view
插件!
更多关于Flutter日历日视图插件calendar_day_view的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter日历日视图插件calendar_day_view的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用calendar_day_view
插件来创建一个日历日视图的一个示例代码。这个示例将展示如何集成该插件并显示一个简单的日视图日历。
首先,确保你已经在pubspec.yaml
文件中添加了calendar_day_view
依赖:
dependencies:
flutter:
sdk: flutter
calendar_day_view: ^最新版本号 # 请替换为实际的最新版本号
然后,运行flutter pub get
来获取依赖。
接下来,在你的Flutter项目中,你可以创建一个简单的页面来展示日历日视图。以下是一个完整的示例代码:
import 'package:flutter/material.dart';
import 'package:calendar_day_view/calendar_day_view.dart';
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Calendar Day View Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CalendarDayViewPage(),
);
}
}
class CalendarDayViewPage extends StatefulWidget {
@override
_CalendarDayViewPageState createState() => _CalendarDayViewPageState();
}
class _CalendarDayViewPageState extends State<CalendarDayViewPage> {
DateTime _selectedDate = DateTime.now();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Calendar Day View Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Selected Date: ${DateFormat('yyyy-MM-dd').format(_selectedDate)}',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 16),
Expanded(
child: CalendarDayView(
initialDate: _selectedDate,
firstDayOfWeek: DateTime.monday,
onDateSelected: (DateTime date) {
setState(() {
_selectedDate = date;
});
},
builder: (context, date, events) {
// 这里你可以自定义每一天的视图
return Container(
decoration: BoxDecoration(
color: date == _selectedDate ? Colors.blue.withOpacity(0.1) : Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
DateFormat('EEE, MMM d').format(date),
style: TextStyle(color: Colors.black, fontSize: 18),
),
),
);
},
),
),
],
),
),
);
}
}
代码解释:
-
依赖导入:首先导入必要的包,包括
flutter/material.dart
、calendar_day_view/calendar_day_view.dart
和intl/intl.dart
用于日期格式化。 -
应用入口:
MyApp
类作为应用的入口,设置了应用的主题和主页面。 -
主页面:
CalendarDayViewPage
是一个有状态的Widget,用于管理日历的选中日期。 -
状态管理:在
_CalendarDayViewPageState
中,有一个_selectedDate
变量来存储当前选中的日期。 -
UI布局:
- 使用
Scaffold
和AppBar
来创建应用的顶部导航栏。 - 使用
Column
来垂直排列文本和日历视图。 - 文本显示当前选中的日期。
Expanded
widget确保日历视图可以扩展以填充剩余的空间。
- 使用
-
日历视图:
CalendarDayView
widget用于显示日历日视图。initialDate
设置初始日期。firstDayOfWeek
设置一周的第一天。onDateSelected
回调用于处理日期选择事件。builder
函数用于自定义每一天的视图,这里简单地根据是否选中日期来改变背景色和显示日期。
运行这个代码,你将看到一个简单的日历日视图,可以滚动选择不同的日期,并且选中的日期会在顶部文本中显示。你可以根据需要进一步自定义和扩展这个示例。