如何在 Flutter 中实现 SingleLine 水平 CalendarView

Posted

技术标签:

【中文标题】如何在 Flutter 中实现 SingleLine 水平 CalendarView【英文标题】:How to achieve SingleLine Horizontal CalendarView in Flutter 【发布时间】:2020-02-02 09:29:56 【问题描述】:

我是 Flutter 的新手。如何在颤动中实现SingleLine水平滚动CalendarView和日期选择。 请找到预期CalendarView 的下图。帮助将不胜感激

Expected CalendarView

【问题讨论】:

【参考方案1】:

你可以使用包https://pub.dev/packages/table_calendar 在月份模式下,只会显示一行日期

代码sn-p

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: '',
      ,

示例代码中,_buildButtons() 和 _buildEventList() 如果不需要,可以备注

body: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          // Switch out 2 lines below to play with TableCalendar's settings
          //-----------------------
          _buildTableCalendar(),
          // _buildTableCalendarWithBuilders(),
          //const SizedBox(height: 8.0),
          //_buildButtons(),
          //const SizedBox(height: 8.0),
          //Expanded(child: _buildEventList()),
        ],
      ),

完整示例代码

//  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/table_calendar.dart';

// Example holidays
final Map<DateTime, List> _holidays = 
  DateTime(2019, 1, 1): ['New Year\'s Day'],
  DateTime(2019, 1, 6): ['Epiphany'],
  DateTime(2019, 2, 14): ['Valentine\'s Day'],
  DateTime(2019, 4, 21): ['Easter Sunday'],
  DateTime(2019, 4, 22): ['Easter Monday'],
;

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


class MyApp extends StatelessWidget 
  @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
  _MyHomePageState createState() => _MyHomePageState();


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

  @override
  void initState() 
    super.initState();
    final _selectedDay = DateTime.now();

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

    _selectedEvents = _events[_selectedDay] ?? [];

    _calendarController = CalendarController();

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

    _animationController.forward();
  

  @override
  void dispose() 
    _animationController.dispose();
    _calendarController.dispose();
    super.dispose();
  

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

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

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          // Switch out 2 lines below to play with TableCalendar's settings
          //-----------------------
          _buildTableCalendar(),
          // _buildTableCalendarWithBuilders(),
          const SizedBox(height: 8.0),
          _buildButtons(),
          const SizedBox(height: 8.0),
          Expanded(child: _buildEventList()),
        ],
      ),
    );
  

  // Simple TableCalendar configuration (using Styles)
  Widget _buildTableCalendar() 
    return TableCalendar(
      calendarController: _calendarController,
      events: _events,
      holidays: _holidays,
      startingDayOfWeek: StartingDayOfWeek.monday,
      calendarStyle: CalendarStyle(
        selectedColor: Colors.deepOrange[400],
        todayColor: Colors.deepOrange[200],
        markersColor: Colors.brown[700],
        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,
    );
  

  // More advanced TableCalendar configuration (using Builders & Styles)
  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 = <Widget>[];

          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) 
        _onDaySelected(date, events);
        _animationController.forward(from: 0.0);
      ,
      onVisibleDaysChanged: _onVisibleDaysChanged,
    );
  

  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() 
    return Column(
      children: <Widget>[
        Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            RaisedButton(
              child: Text('month'),
              onPressed: () 
                setState(() 
                  _calendarController.setCalendarFormat(CalendarFormat.month);
                );
              ,
            ),
            RaisedButton(
              child: Text('2 weeks'),
              onPressed: () 
                setState(() 
                  _calendarController.setCalendarFormat(CalendarFormat.twoWeeks);
                );
              ,
            ),
            RaisedButton(
              child: Text('week'),
              onPressed: () 
                setState(() 
                  _calendarController.setCalendarFormat(CalendarFormat.week);
                );
              ,
            ),
          ],
        ),
        const SizedBox(height: 8.0),
        RaisedButton(
          child: Text('setDay 10-07-2019'),
          onPressed: () 
            _calendarController.setSelectedDay(DateTime(2019, 7, 10), 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 tapped!'),
                ),
              ))
          .toList(),
    );
  

【讨论】:

在这里,我只想显示 SigleLine 并且必须从 HeaderStyle 中删除(月、周和周)选项我只想只有月,帮帮我 你的意思是availableCalendarFormats: const CalendarFormat.month: '',你只需要月份吗?你可以直接删除周 是的,我只需要一个月 initialCalendarFormat: CalendarFormat.week, availableCalendarFormats: const CalendarFormat.week: 'Week', , 将 initialCalendarFormat: 更改为 CalendarFormat.week 并添加 availableCalendarFormats ,就可以了

以上是关于如何在 Flutter 中实现 SingleLine 水平 CalendarView的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 中实现应用内购买订阅?

如何在 Flutter 的消息中实现用户提及?

如何在 Flutter 中实现 GooglePlay TabBar?

如何在 Flutter 中实现“磨砂玻璃”效果?

如何在flutter中实现agora视频通话邀请

如何在 Flutter Web 中实现 Stripe 支付?