Flutter分页垂直日历插件paged_vertical_calendar的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter分页垂直日历插件paged_vertical_calendar的使用

📆 Paged Vertical Calendar 📆

Pub GitHub stars GitHub last commit

paged_vertical_calendar 是一个用于实现基于日历界面的简单分页框架。

gif showing of package customizability gif showing dat range picker example gif showing paged data example

🔨 工作原理

paged_vertical_calendar 是一个非常简约的框架,它会根据滚动行为自动加载月份。它提供了许多有用的回调函数来实现自定义的日历交互,并提供构建器来自定义日历外观。请查看示例以了解日期范围选择和分页数据可视化的几种实现方式。

PagedVerticalCalendar 没有必需的参数,只要提供固定的高度即可使用。

Scaffold(
  body: PagedVerticalCalendar(),
);

📢 功能

以下是 PagedVerticalCalendar 提供的一些功能:

PagedVerticalCalendar(
  minDate: DateTime.now().subtract(Duration(days: 365)),
  maxDate: DateTime.now().add(Duration(days: 365)),
  initialDate: DateTime.now().add(Duration(days: 3)),
  invisibleMonthsThreshold: 1,
  startWeekWithSunday: true,
  onMonthLoaded: (year, month) {
    // 当月加载时触发
  },
  onDayPressed: (value) {
    // 当点击某天时触发
  },
  onPaginationCompleted: (direction) {
    // 分页完成时触发
  },
);
  • onMonthLoaded: 每当添加一个月到列表时触发此回调。可以通过设置 invisibleMonthsThreshold 参数来调整触发时间。
  • invisibleMonthsThreshold: 决定在视图外应加载多少个月份。换句话说,用户滚动到该位置之前应预加载多少个月份,默认值为 1
  • onDayPressed: 简单的 onPressed 回调,同时提供被点击日期的 DateTime
  • startWeekWithSunday: 如果应用程序针对周日开始的国家/地区进行本地化,可以将此参数设置为 true
  • minDate: 提供最小日期后,日历将在该日期停止向上滚动。确保 minDate < maxDate
  • maxDate: 提供最大日期后,日历将在该日期停止向下滚动。确保 maxDate > minDate
  • initialDate: 提供初始日期后,日历将从该日期开始显示(否则为 DateTime.now())。确保 minDate < initialData < maxDate
  • onPaginationCompleted: 当提供 minDatemaxDate 时,此回调会在分页完成后触发,并返回 PaginationDirection 以指示达到日历的哪一侧。

🎨 自定义

PagedVerticalCalendar 提供默认的日历样式,但这些样式可以完全自定义。为此,提供了多个构建器:

PagedVerticalCalendar(
  monthBuilder: (context, month, year) {
    // 提供一个月份标题小部件
  },
  dayBuilder: (context, date) {
    // 提供一个日期小部件
  },
  listPadding: // 提供 EdgeInset 值
);
  • monthBuilder: 提供年份和月份作为整数。此构建器必须返回一个形成每月标题的小部件,intl 包在这里用于日期格式化效果很好。
  • dayBuilder: 提供日期作为 DateTime。此构建器将为每一天调用一次。通常至少需要提供一个包含当前日期编号的文本小部件。
  • listPadding: 可以提供边距以确保列表具有内边距。

👋 参与其中

如果这个包对你有用,请在 pub.dev 上点赞并在 GitHub 上加星。如果你有任何问题、建议或拉取请求,我非常愿意看到它们!

示例代码

以下是一个完整的示例代码,展示了如何使用 paged_vertical_calendar 实现自定义日历、日期选择器和分页数据展示。

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:paged_vertical_calendar/paged_vertical_calendar.dart';

void main() => runApp(Home());

/// 一个简单的例子,展示了如何使用此包实现日历相关界面。
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: DefaultTabController(
          length: 3,
          child: Scaffold(
            appBar: AppBar(
              title: const Text('Paged Vertical Calendar'),
              bottom: TabBar(
                indicatorSize: TabBarIndicatorSize.label,
                tabs: [
                  Tab(icon: Icon(Icons.calendar_today), text: 'Custom'),
                  Tab(icon: Icon(Icons.date_range), text: 'DatePicker'),
                  Tab(icon: Icon(Icons.dns), text: 'Pagination'),
                ],
              ),
            ),
            body: TabBarView(
              children: [
                Custom(),
                DatePicker(),
                Pagination(),
              ],
            ),
          ),
        ),
      );
}

/// 展示日历可定制性的简单示例。
class Custom extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return PagedVerticalCalendar(
      startWeekWithSunday: true,

      /// 自定义月份头部外观,添加一周指示器。
      monthBuilder: (context, month, year) {
        return Column(
          children: [
            /// 创建一个自定义标题,显示月份和年份。
            Container(
              padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
              margin: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                color: Theme.of(context).primaryColor,
                borderRadius: BorderRadius.all(Radius.circular(50)),
              ),
              child: Text(
                DateFormat('MMMM yyyy').format(DateTime(year, month)),
                style: Theme.of(context).textTheme.bodyLarge!.copyWith(
                      color: Colors.white,
                    ),
              ),
            ),

            /// 添加一行显示星期几。
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20.0),
              child: Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  weekText('Su'),
                  weekText('Mo'),
                  weekText('Tu'),
                  weekText('We'),
                  weekText('Th'),
                  weekText('Fr'),
                  weekText('Sa'),
                ],
              ),
            ),
          ],
        );
      },

      /// 在每周之间添加一条线。
      dayBuilder: (context, date) {
        return Column(
          children: [
            Text(DateFormat('d').format(date)),
            const Divider(),
          ],
        );
      },
    );
  }

  Widget weekText(String text) {
    return Padding(
      padding: const EdgeInsets.all(4.0),
      child: Text(
        text,
        style: TextStyle(color: Colors.grey, fontSize: 10),
      ),
    );
  }
}

/// 显示如何制作基本的日期范围选择器并带有UI指示。
class DatePicker extends StatefulWidget {
  @override
  _DatePickerState createState() => _DatePickerState();
}

class _DatePickerState extends State<DatePicker> {
  /// 存储选中的开始和结束日期。
  DateTime? start;
  DateTime? end;

  /// 方法用于检查某天是否在选中范围内,用于高亮显示。
  bool isInRange(DateTime date) {
    if (start == null) return false;
    if (end == null) return date == start;
    return ((date == start || date.isAfter(start!)) &&
        (date == end || date.isBefore(end!)));
  }

  @override
  Widget build(BuildContext context) {
    return PagedVerticalCalendar(
      addAutomaticKeepAlives: true,
      dayBuilder: (context, date) {
        final color = isInRange(date) ? Colors.green : Colors.transparent;

        return Container(
          color: color,
          child: Center(
            child: Text(DateFormat('d').format(date)),
          ),
        );
      },
      onDayPressed: (date) {
        setState(() {
          if (start == null)
            start = date;
          else if (end == null)
            end = date;
          else {
            print('selected range from $start to $end');
            start = null;
            end = null;
          }
        });
      },
    );
  }
}

/// 显示如何在日历中显示分页数据并与之交互。
class Pagination extends StatefulWidget {
  @override
  _PaginationState createState() => _PaginationState();
}

class _PaginationState extends State<Pagination> {
  /// 存储所有要显示的项目。
  List<DateTime> items = [];

  /// 每次加载新月份时调用。
  void fetchNewEvents(int year, int month) async {
    Random random = Random();
    final newItems = List<DateTime>.generate(random.nextInt(40), (i) {
      return DateTime(year, month, random.nextInt(27) + 1);
    });

    setState(() => items.addAll(newItems));
  }

  @override
  Widget build(BuildContext context) {
    return PagedVerticalCalendar(
      addAutomaticKeepAlives: true,
      onMonthLoaded: fetchNewEvents,
      dayBuilder: (context, date) {
        final eventsThisDay = items.where((e) => e == date);

        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(DateFormat('d').format(date)),
            Wrap(
              children: eventsThisDay.map((event) {
                return Padding(
                  padding: const EdgeInsets.all(1),
                  child: CircleAvatar(
                    radius: 5,
                    backgroundColor: Colors.red,
                  ),
                );
              }).toList(),
            )
          ],
        );
      },
      onDayPressed: (day) {
        final eventsThisDay = items.where((e) => e == day);
        print('items this day: $eventsThisDay');
      },
    );
  }
}

希望这段内容能帮助你更好地理解和使用 paged_vertical_calendar 插件!如果有任何问题或需要进一步的帮助,请随时提问。


更多关于Flutter分页垂直日历插件paged_vertical_calendar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分页垂直日历插件paged_vertical_calendar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 paged_vertical_calendar Flutter 插件的示例代码。这个插件允许你创建一个分页的垂直日历,非常适合用于展示日期选择或日程安排等功能。

首先,确保你已经在 pubspec.yaml 文件中添加了 paged_vertical_calendar 依赖:

dependencies:
  flutter:
    sdk: flutter
  paged_vertical_calendar: ^最新版本号  # 请替换为最新版本号

然后,运行 flutter pub get 来获取依赖。

以下是一个完整的 Flutter 应用示例,展示了如何使用 paged_vertical_calendar

import 'package:flutter/material.dart';
import 'package:paged_vertical_calendar/paged_vertical_calendar.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Paged Vertical Calendar Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: CalendarScreen(),
    );
  }
}

class CalendarScreen extends StatefulWidget {
  @override
  _CalendarScreenState createState() => _CalendarScreenState();
}

class _CalendarScreenState extends State<CalendarScreen> {
  final PageController _pageController = PageController();
  DateTime _selectedDate = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Paged Vertical Calendar'),
      ),
      body: Column(
        children: [
          Expanded(
            child: PagedVerticalCalendar(
              controller: _pageController,
              firstDate: DateTime(2023, 1, 1),
              lastDate: DateTime(2023, 12, 31),
              initialDate: _selectedDate,
              selectedDate: _selectedDate,
              onDateSelected: (date) {
                setState(() {
                  _selectedDate = date;
                });
              },
              onPageChanged: (page) {
                // 可选:处理页面变化
              },
              weekdayTextStyle: TextStyle(color: Colors.black54, fontSize: 12),
              dayTextStyle: TextStyle(color: Colors.black, fontSize: 16),
              todayTextStyle: TextStyle(color: Colors.red, fontSize: 16, fontWeight: FontWeight.bold),
              selectedTextStyle: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
              selectedDecoration: BoxDecoration(
                color: Colors.blue,
                shape: BoxShape.circle,
              ),
              todayDecoration: BoxDecoration(
                color: Colors.grey.withOpacity(0.3),
                shape: BoxShape.circle,
              ),
              weekdayHeight: 20,
              dayHeight: 40,
              showWeekday: true,
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Selected Date: ${_selectedDate.toLocal()}',
              style: TextStyle(fontSize: 18),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }
}

解释

  1. 依赖引入:在 pubspec.yaml 中添加 paged_vertical_calendar 依赖。

  2. 主应用:在 MyApp 中设置基本的 Material 应用主题,并指向 CalendarScreen

  3. 日历屏幕CalendarScreen 是一个 StatefulWidget,它管理日历的选中日期和分页控制器。

  4. PagedVerticalCalendar

    • controller:分页控制器。
    • firstDatelastDate:日历显示的范围。
    • initialDateselectedDate:初始和选中的日期。
    • onDateSelected:选中日期时的回调。
    • onPageChanged:页面变化时的回调(可选)。
    • weekdayTextStyledayTextStyletodayTextStyleselectedTextStyle:设置不同日期的文本样式。
    • selectedDecorationtodayDecoration:设置选中日期和今天的装饰。
    • weekdayHeightdayHeight:设置星期和日期的高度。
    • showWeekday:是否显示星期。
  5. 状态管理:在 dispose 方法中释放分页控制器资源。

这个示例展示了如何使用 paged_vertical_calendar 来创建一个基本的垂直分页日历,你可以根据需要进行进一步的自定义和扩展。

回到顶部