Flutter日历插件table_calendar_null_safe的使用

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

Flutter日历插件table_calendar_null_safe的使用

功能特性

  • 广泛且易于使用的API
  • 真正灵活的UI自定义构建器
  • 完全的程序化控制通过CalendarController
  • 动态事件
  • 节假日接口
  • 语言本地化支持
  • 垂直自动调整大小
  • 美丽的动画
  • 手势处理
  • 多种日历格式
  • 多种星期格式
  • 指定可用日期范围
  • 出色且可配置的UI

安装

pubspec.yaml 文件中添加依赖:

dependencies:
  table_calendar_null_safe: ^2.0.0

然后在你的项目中导入它:

import 'package:table_calendar_null_safe/table_calendar.dart';

最后创建一个带有CalendarControllerTableCalendar:

class _MyHomePageState extends State<MyHomePage> {
  late CalendarController _calendarController;

  [@override](/user/override)
  void initState() {
    super.initState();
    _calendarController = CalendarController();
  }

  [@override](/user/override)
  void dispose() {
    _calendarController.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return TableCalendar(
      calendarController: _calendarController,
    );
  }
}

语言本地化

Table Calendar 支持多种语言。要将日历显示为特定语言,请使用locale属性。如果不指定,将使用默认语言。

TableCalendar(
  calendarController: _calendarController,
  locale: 'zh_CN', // 设置为中文
);

示例代码

以下是完整的示例代码,展示了如何使用table_calendar_null_safe插件创建一个功能丰富的日历应用。

// Copyright (c) 2019 Aleksander Woźniak
// Licensed under Apache License v2.0

import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:table_calendar_null_safe/table_calendar_null_safe.dart';

// 示例节假日
final Map<DateTime, List> _holidays = {
  DateTime(2020, 1, 1): ['新年'],
  DateTime(2020, 1, 6): ['主显节'],
  DateTime(2020, 2, 14): ['情人节'],
  DateTime(2020, 4, 21): ['复活节星期天'],
  DateTime(2020, 4, 22): ['复活节星期一'],
};

void main() {
  initializeDateFormatting().then((_) => runApp(MyApp()));
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Table Calendar Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Table Calendar Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);

  final String? title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  Map<DateTime, List>? _events;
  List? _selectedEvents;
  late AnimationController _animationController;
  late CalendarController _calendarController;

  [@override](/user/override)
  void initState() {
    super.initState();
    final _selectedDay = DateTime.now();

    _events = {
      _selectedDay.subtract(Duration(days: 30)): [
        '活动A0',
        '活动B0',
        '活动C0'
      ],
      _selectedDay.subtract(Duration(days: 27)): ['活动A1'],
      _selectedDay.subtract(Duration(days: 20)): [
        '活动A2',
        '活动B2',
        '活动C2',
        '活动D2'
      ],
      _selectedDay.subtract(Duration(days: 16)): ['活动A3', '活动B3'],
      _selectedDay.subtract(Duration(days: 10)): [
        '活动A4',
        '活动B4',
        '活动C4'
      ],
      _selectedDay.subtract(Duration(days: 4)): [
        '活动A5',
        '活动B5',
        '活动C5'
      ],
      _selectedDay.subtract(Duration(days: 2)): ['活动A6', '活动B6'],
      _selectedDay: ['活动A7', '活动B7', '活动C7', '活动D7'],
      _selectedDay.add(Duration(days: 1)): [
        '活动A8',
        '活动B8',
        '活动C8',
        '活动D8'
      ],
      _selectedDay.add(Duration(days: 3)):
          Set.from(['活动A9', '活动A9', '活动B9']).toList(),
      _selectedDay.add(Duration(days: 7)): [
        '活动A10',
        '活动B10',
        '活动C10'
      ],
      _selectedDay.add(Duration(days: 11)): ['活动A11', '活动B11'],
      _selectedDay.add(Duration(days: 17)): [
        '活动A12',
        '活动B12',
        '活动C12',
        '活动D12'
      ],
      _selectedDay.add(Duration(days: 22)): ['活动A13', '活动B13'],
      _selectedDay.add(Duration(days: 26)): [
        '活动A14',
        '活动B14',
        '活动C14'
      ],
    };

    _selectedEvents = _events?[_selectedDay] ?? [];
    _calendarController = CalendarController();

    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );

    _animationController.forward();
  }

  [@override](/user/override)
  void dispose() {
    _animationController.dispose();
    _calendarController.dispose();
    super.dispose();
  }

  void _onDaySelected(DateTime day, List events, List holidays) {
    print('CALLBACK: _onDaySelected');
    setState(() {
      _selectedEvents = events;
    });
  }

  void _onVisibleDaysChanged(
      DateTime first, DateTime last, CalendarFormat format) {
    print('CALLBACK: _onVisibleDaysChanged');
  }

  void _onCalendarCreated(
      DateTime first, DateTime last, CalendarFormat format) {
    print('CALLBACK: _onCalendarCreated');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title ?? '标题'),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          // 切换以下两行以更改TableCalendar的设置
          //-----------------------
          _buildTableCalendar(),
          // _buildTableCalendarWithBuilders(),
          const SizedBox(height: 8.0),
          _buildButtons(),
          const SizedBox(height: 8.0),
          Expanded(child: _buildEventList()),
        ],
      ),
    );
  }

  // 简单的TableCalendar配置(使用样式)
  Widget _buildTableCalendar() {
    return TableCalendar(
      calendarController: _calendarController,
      events: _events ?? {},
      holidays: _holidays,
      startingDayOfWeek: StartingDayOfWeek.monday,
      calendarStyle: CalendarStyle(
        selectedColor: Colors.deepOrange,
        todayColor: Colors.deepOrange,
        markersColor: Colors.brown,
        outsideDaysVisible: false,
      ),
      headerStyle: HeaderStyle(
        formatButtonTextStyle:
            TextStyle().copyWith(color: Colors.white, fontSize: 15.0),
        formatButtonDecoration: BoxDecoration(
          color: Colors.deepOrange[400],
          borderRadius: BorderRadius.circular(16.0),
        ),
      ),
      onDaySelected: _onDaySelected,
      onVisibleDaysChanged: _onVisibleDaysChanged,
      onCalendarCreated: _onCalendarCreated,
    );
  }

  // 更高级的TableCalendar配置(使用构建器和样式)
  /*
  Widget _buildTableCalendarWithBuilders() {
    return TableCalendar(
      locale: 'pl_PL',
      calendarController: _calendarController,
      events: _events ?? {},
      holidays: _holidays,
      initialCalendarFormat: CalendarFormat.month,
      formatAnimation: FormatAnimation.slide,
      startingDayOfWeek: StartingDayOfWeek.sunday,
      availableGestures: AvailableGestures.all,
      availableCalendarFormats: const {
        CalendarFormat.month: '',
        CalendarFormat.week: '',
      },
      calendarStyle: CalendarStyle(
        outsideDaysVisible: false,
        weekendStyle: TextStyle().copyWith(color: Colors.blue[800]),
        holidayStyle: TextStyle().copyWith(color: Colors.blue[800]),
      ),
      daysOfWeekStyle: DaysOfWeekStyle(
        weekendStyle: TextStyle().copyWith(color: Colors.blue[600]),
      ),
      headerStyle: HeaderStyle(
        centerHeaderTitle: true,
        formatButtonVisible: false,
      ),
      builders: CalendarBuilders(
        selectedDayBuilder: (context, date, _) {
          return FadeTransition(
            opacity: Tween(begin: 0.0, end: 1.0).animate(_animationController),
            child: Container(
              margin: const EdgeInsets.all(4.0),
              padding: const EdgeInsets.only(top: 5.0, left: 6.0),
              color: Colors.deepOrange[300],
              width: 100,
              height: 100,
              child: Text(
                '${date.day}',
                style: TextStyle().copyWith(fontSize: 16.0),
              ),
            ),
          );
        },
        todayDayBuilder: (context, date, _) {
          return Container(
            margin: const EdgeInsets.all(4.0),
            padding: const EdgeInsets.only(top: 5.0, left: 6.0),
            color: Colors.amber[400],
            width: 100,
            height: 100,
            child: Text(
              '${date.day}',
              style: TextStyle().copyWith(fontSize: 16.0),
            ),
          );
        },
        markersBuilder: (context, date, events, holidays) {
          final children = [];

          if (events.isNotEmpty) {
            children.add(
              Positioned(
                right: 1,
                bottom: 1,
                child: _buildEventsMarker(date, events),
              ),
            );
          }

          if (holidays.isNotEmpty) {
            children.add(
              Positioned(
                right: -2,
                top: -2,
                child: _buildHolidaysMarker(),
              ),
            );
          }

          return children;
        },
      ),
      onDaySelected: (date, events, holidays) {
        _onDaySelected(date, events, holidays);
        _animationController.forward(from: 0.0);
      },
      onVisibleDaysChanged: _onVisibleDaysChanged,
      onCalendarCreated: _onCalendarCreated,
    );
  }

  Widget _buildEventsMarker(DateTime date, List events) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 300),
      decoration: BoxDecoration(
        shape: BoxShape.rectangle,
        color: _calendarController.isSelected(date)
            ? Colors.brown[500]
            : _calendarController.isToday(date)
                ? Colors.brown[300]
                : Colors.blue[400],
      ),
      width: 16.0,
      height: 16.0,
      child: Center(
        child: Text(
          '${events.length}',
          style: TextStyle().copyWith(
            color: Colors.white,
            fontSize: 12.0,
          ),
        ),
      ),
    );
  }

  Widget _buildHolidaysMarker() {
    return Icon(
      Icons.add_box,
      size: 20.0,
      color: Colors.blueGrey[800],
    );
  }
  */

  Widget _buildButtons() {
    final dateTime =
        _events?.keys.elementAt((_events?.length ?? 2) - 2) ?? DateTime.now();

    return Column(
      children: <Widget>[
        Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            TextButton(
              child: Text('月视图'),
              onPressed: () {
                setState(() {
                  _calendarController.setCalendarFormat(CalendarFormat.month);
                });
              },
            ),
            TextButton(
              child: Text('两周视图'),
              onPressed: () {
                setState(() {
                  _calendarController.setCalendarFormat(CalendarFormat.twoWeeks);
                });
              },
            ),
            TextButton(
              child: Text('周视图'),
              onPressed: () {
                setState(() {
                  _calendarController.setCalendarFormat(CalendarFormat.week);
                });
              },
            ),
          ],
        ),
        const SizedBox(height: 8.0),
        TextButton(
          child: Text(
              '设置日期 ${dateTime.day}-${dateTime.month}-${dateTime.year}'),
          onPressed: () {
            _calendarController.setSelectedDay(
              DateTime(dateTime.year, dateTime.month, dateTime.day),
              runCallback: true,
            );
          },
        ),
      ],
    );
  }

  Widget _buildEventList() {
    return ListView(
      children: _selectedEvents
              ?.map((event) => Container(
                    decoration: BoxDecoration(
                      border: Border.all(width: 0.8),
                      borderRadius: BorderRadius.circular(12.0),
                    ),
                    margin: const EdgeInsets.symmetric(
                        horizontal: 8.0, vertical: 4.0),
                    child: ListTile(
                      title: Text(event.toString()),
                      onTap: () => print('$event 被点击!'),
                    ),
                  ))
              .toList() ??
          [],
    );
  }
}

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

1 回复

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


当然,以下是一个关于如何使用table_calendar_null_safe插件的Flutter代码示例。这个插件允许你在Flutter应用中创建一个可定制的日历视图。请确保你已经在pubspec.yaml文件中添加了依赖:

dependencies:
  flutter:
    sdk: flutter
  table_calendar: ^3.0.0  # 请检查最新版本号

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

以下是一个完整的示例代码,展示了如何使用table_calendar_null_safe插件来创建一个基本的日历视图:

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

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

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

class CalendarPage extends StatefulWidget {
  @override
  _CalendarPageState createState() => _CalendarPageState();
}

class _CalendarPageState extends State<CalendarPage> with SingleTickerProviderStateMixin {
  CalendarController _controller;
  DateTime _selectedDay = DateTime.now();
  Map<DateTime, List<String>> _events;

  @override
  void initState() {
    super.initState();
    _controller = CalendarController();

    // 初始化事件数据
    _events = {};

    // 添加一些示例事件
    _addEvent('2023-10-05', ['Event 1']);
    _addEvent('2023-10-07', ['Event 2', 'Event 3']);
    _addEvent('2023-10-15', ['Event 4']);
    // 你可以根据需要添加更多事件

    // 设置初始选择的日期为今天
    _controller.setSelectedDay(_selectedDay, runCallback: false);
  }

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

  void _addEvent(String date, List<String> events) {
    DateTime eventDate = DateTime.parse(date);
    if (!_events.containsKey(eventDate)) {
      _events[eventDate] = [];
    }
    _events[eventDate]!.addAll(events);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Calendar Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            TableCalendar(
              _controller,
              locale: 'en_US',
              eventLoader: _loadEvents,
              startingDayOfWeek: StartingDayOfWeek.monday,
              availableCalendarFormats: const {
                CalendarFormat.month: '',
                CalendarFormat.week: '',
              },
              calendarFormat: _controller.calendarFormat,
              onFormatChanged: (CalendarFormat format) {
                if (_controller.calendarFormat != format) {
                  setState(() {
                    _controller.calendarFormat = format;
                  });
                }
              },
              onSelectedDayChanged: (DateTime day, DateTime focusedDay) {
                if (focusedDay != null && focusedDay != _selectedDay) {
                  setState(() {
                    _selectedDay = focusedDay;
                  });
                  _controller.setSelectedDay(focusedDay, runCallback: false);
                }
              },
              onCalendarCreated: (DateTime firstDay, DateTime lastDay) {
                _loadEvents(firstDay, lastDay);
              },
            ),
            _buildEventList(),
          ],
        ),
      ),
    );
  }

  Future<void> _loadEvents(DateTime start, DateTime end) async {
    // 这里可以根据需要加载事件数据,这里为了简单起见,直接使用已有的_events数据
    List<DateTime> daysWithEvents = _events.keys
        .where((eventDay) => eventDay.isAfter(start.subtract(const Duration(days: 1))) && eventDay.isBefore(end.add(const Duration(days: 1))))
        .toList();

    // 通知控制器哪些日期有事件
    _controller.setEvents(daysWithEvents);
  }

  Widget _buildEventList() {
    return _events[_selectedDay]?.length > 0
        ? Container(
            decoration: BoxDecoration(
              border: Border.all(color: Colors.grey),
              borderRadius: BorderRadius.circular(10),
            ),
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: _events[_selectedDay]!.map((event) {
                  return Text(
                    event,
                    style: TextStyle(decoration: TextDecoration.underline),
                  );
                }).toList(),
              ),
            ),
          )
        : Container();
  }
}

在这个示例中,我们创建了一个简单的日历应用,其中:

  1. CalendarController用于管理日历的状态。
  2. _events字典存储了日期和事件列表的映射。
  3. _addEvent方法用于添加事件。
  4. TableCalendar小部件用于显示日历,并处理用户交互。
  5. _buildEventList方法用于在选定日期下方显示事件列表。

你可以根据需要进一步自定义和扩展这个示例。

回到顶部