Flutter自定义日历【Flutter 专题 11】

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们使用的日历多年来一直在发展。从手写日历到印刷日历,我们现在手上都有一个数字日历,它是高度可定制的,并在我们需要提醒的准确时刻提醒我们我们的事件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们将看到如何在 Flutter 中构建和自定义日历小部件,以便为我们的用户提供这种体验。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尽管 Flutter 以日期和时间选择器的形式提供了一个日历小部件,它提供了可自定义的颜色、字体和用法,但它缺少一些功能。您可以使用它来选择日期和时间(或两者)并将其添加到您的应用程序中,但它需要与一个按钮和一个占位符相结合,可以保存选择的日期或时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我将从 Flutter 架构提供的原生日历开始,然后转到pub.dev 上最流行的日历小部件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"。您还可以使用许多其他流行的日历小部件,但在本教程中,我们将深入介绍。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Flutter 日历小部件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TableCalendar ()","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Flutter 日历小部件(日期选择器和时间选择器)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了更彻底地解释这个小部件,","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,让我们回顾一下","attrs":{}},{"type":"link","attrs":{"href":"https://api.flutter.dev/flutter/material/showDatePicker.html","title":"","type":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"showDatePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"默认构造函数","attrs":{}}]},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"showDatePicker({\n// it requires a context\n required BuildContext context, \n// when datePicker is displayed, it will show month of the current date\n required DateTime initialDate, \n// earliest possible date to be displayed (eg: 2000)\n required DateTime firstDate,\n// latest allowed date to be displayed (eg: 2050)\n required DateTime lastDate,\n// it represents TODAY and it will be highlighted\n DateTime? currentDate,\n // either by input or selected, defaults to calendar mode.\n DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar or input,\n// restricts user to select date from range to dates.\n SelectableDayPredicate? selectableDayPredicate,\n// text that is displayed at the top of the datePicker\n String? helpText,\n// text that is displayed on cancel button\n String? cancelText,\n// text that is displayed on confirm button\n String? confirmText,\n// use builder function to customise the datePicker \n TransitionBuilder? Builder,\n// option to display datePicker in year or day mode. Defaults to day\n DatePickerMode initialDatePickerMode = DatePickerMode.day or year,\n// error message displayed when user hasn't entered date in proper format\n String? errorFormatText,\n// error message displayed when date is not selectable\n String? errorInvalidText,\n// hint message displayed to prompt user to enter date according to the format mentioned (eg: dd/mm/yyyy)\n String? fieldHintText,\n// label message displayed for what the user is entering date for (eg: birthdate)\n String? fieldLabelText,\n})\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于上面的默认构造函数,大家可以参考下图,我已经指出了一些重要的属性,可以根据自己的需要进行自定义。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/2837edd93c38331dfdbbb6de797ab268.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"它是如何工作的?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我会先给答案加整个代码,以作展示,用户可以输入会议名称和链接,然后选择日期和时间。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"整个代码","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/781ffdcd4ae2a1c9fca4ea54fcf1477d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"import 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\n\n\nconst Color darkBlue = Color.fromARGB(255, 18, 32, 47);\n\nvoid main() {\n runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialApp(\n theme: ThemeData.dark().copyWith(\n scaffoldBackgroundColor: darkBlue,\n ),\n debugShowCheckedModeBanner: false,\n home: FlutterDatePickerExample());\n }\n}\n\nclass FlutterDatePickerExample extends StatelessWidget {\n final ValueNotifier dateSub = ValueNotifier(null);\n final ValueNotifier longDateSub = ValueNotifier(null);\n final ValueNotifier timeSub = ValueNotifier(null);\n final ValueNotifier timeSubShort = ValueNotifier(null);\n final TextEditingController meetingName = TextEditingController();\n final TextEditingController meetingLink = TextEditingController();\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: const Text('Vanilla Calendar Flutter'),\n ),\n body: Padding(\n padding: const EdgeInsets.all(14.0),\n child: SingleChildScrollView(\n child:Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n children: [\n const Text(\n ' 创建会议',\n textAlign: TextAlign.center,\n style: TextStyle(fontSize: 24.0),\n ),\n const SizedBox(\n height: 20,\n ),\n buildTextField(controller: meetingName, hint: '输入会议名称'),\n const SizedBox(\n height: 20,\n ),\n buildTextField(controller: meetingLink, hint: '输入会议链接'),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' Short Date',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: dateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n currentDate: DateTime.now(),\n initialEntryMode: DatePickerEntryMode.calendar,\n initialDatePickerMode: DatePickerMode.day,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context).copyWith(\n colorScheme: const ColorScheme.light(\n primary: Colors.blueGrey,\n onSurface: AppColors.blackCoffee,\n )\n ),\n child: child!,\n );\n });\n dateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? convertDate(dateVal) : ''));\n }),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' 12H Format Time',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: timeSubShort,\n builder: (context, timeVal, child) {\n return InkWell(\n onTap: () async {\n TimeOfDay? time = await showTimePicker(\n context: context,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context)\n child: child!,\n );\n },\n initialTime: TimeOfDay.now(),\n );\n timeSubShort.value = time;\n },\n child: buildDateTimePicker(timeVal != null\n ? convertTime(timeVal)\n : ''));\n }),\n const SizedBox(\n height: 20.0,\n ),\n const Text(\n ' Long Date',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: longDateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n builder: (context, child) {\n return Theme(\n data: Theme.of(context),\n child: child!,\n );\n });\n longDateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? longDate(dateVal) : ''));\n }),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' 24H Format Time',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: timeSub,\n builder: (context, timeVal, child) {\n return InkWell(\n onTap: () async {\n TimeOfDay? time = await showTimePicker(\n context: context,\n builder: (context, child) {\n return MediaQuery(\n data: MediaQuery.of(context).copyWith(\n alwaysUse24HourFormat: true),\n child: child!,\n );\n },\n initialTime: TimeOfDay.now(),\n );\n timeSub.value = time;\n },\n child: buildDateTimePicker(timeVal != null\n ? timeVal.format(context)\n : ''));\n }),\n const SizedBox(height: 20.0,),\n ElevatedButton(onPressed: () {\n ScaffoldMessenger.of(context).showSnackBar(const SnackBar(\n content: Text('会议创建成功'),\n duration: Duration(seconds: 5),));\n }, child: const Text('提交')),\n ],\n ),\n ),\n \n ),\n );\n }\n\n String convertDate(DateTime dateTime) {\n return DateFormat('yyyy年MM月dd日').format(dateTime);\n }\n\n String longDate(DateTime dateTime) {\n return DateFormat('EEE, MMM d, yyy').format(dateTime);\n }\n\n String convertTime(TimeOfDay timeOfDay) {\n DateTime tempDate = DateFormat('hh:mm').parse(\n timeOfDay.hour.toString() + ':' + timeOfDay.minute.toString());\n var dateFormat = DateFormat('h:mm a');\n return dateFormat.format(tempDate);\n }\n\n\n Widget buildDateTimePicker(String data) {\n return ListTile(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.circular(10.0),\n side: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n ),\n title: Text(data),\n trailing: const Icon(\n Icons.calendar_today,\n color: AppColors.eggPlant,\n ),\n );\n }\n\n Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n }\n \n}\n class AppColors {\n AppColors._();\n\n static const Color blackCoffee = Color(0xFF352d39);\n static const Color eggPlant = Color(0xFF6d435a);\n static const Color celeste = Color(0xFFb1ede8);\n static const Color babyPowder = Color(0xFFFFFcF9);\n static const Color ultraRed = Color(0xFFFF6978);\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"第 1 步:实施 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueNotifier","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已经实现了一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueNotifier","attrs":{}}],"attrs":{}},{"type":"text","text":"将在文本字段中保存日期的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"final ValueNotifier dateSub = ValueNotifier(null);\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"第 2 步:创建","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"datePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"对话框","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"随着","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueListenerBuilder","attrs":{}}],"attrs":{}},{"type":"text","text":"和实例","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DateTime","attrs":{}}],"attrs":{}},{"type":"text","text":",并与InkWell","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"小部件,当我们点击","attrs":{}}],"attrs":{}},{"type":"text","text":"textField","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":",一个","attrs":{}}],"attrs":{}},{"type":"text","text":"datePicker","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"对话框会弹出。当用户点击所需的日期时,它将显示在","attrs":{}}],"attrs":{}},{"type":"text","text":"textField`:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"ValueListenableBuilder(\n valueListenable: dateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n currentDate: DateTime.now(),\n initialEntryMode: DatePickerEntryMode.calendar,\n initialDatePickerMode: DatePickerMode.day,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context).copyWith(\n colorScheme: ColorScheme.fromSwatch(\n primarySwatch: Colors.blueGrey,\n accentColor: AppColors.blackCoffee,\n backgroundColor: Colors.lightBlue,\n cardColor: Colors.white,\n )\n ),\n child: child!,\n );\n });\n dateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? convertDate(dateVal) : ''));\n }),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"buildDateTimePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"只不过是一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"listTile","attrs":{}}],"attrs":{}},{"type":"text","text":"带有自定义边框和日历图标:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"Widget buildDateTimePicker(String data) {\n return ListTile(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.circular(10.0),\n side: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n ),\n title: Text(data),\n trailing: const Icon(\n Icons.calendar_today,\n color: AppColors.eggPlant,\n ),\n );\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们还有一个字符串方法可以将日期转换为所需的格式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"String convertDate(DateTime dateTime) {\n return DateFormat('dd/MM/yyyy').format(dateTime);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这是代码实现时的样子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/31/313b45904d4de8466c3832f53c4dec28.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,让我们回到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"我之前讨论过的,我们将如何实现它,以及我们将如何定制它以满足应用程序的需求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有几种自定义可能性,讨论所有这些都超出了本文的范围。因此,我会尽量做到具体,只讨论其中最重要的部分。当然,也有我亲自试验过的代码实现,以及可供参考的图像。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"表日历","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安装非常简单:您需要从这里复制并粘贴","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pubspec.yaml","attrs":{}}],"attrs":{}},{"type":"text","text":"文件中的依赖项","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"table_calendar","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最新版本是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"table_calendar: ^3.0.2\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我将把它的构造函数分成三个部分:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"设置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"根据您的应用程序需求设计日历样式","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"将事件添加到日历","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样您就可以轻松理解代码并知道如何成功实现它。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 1 步:设置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SingleChildScrollView","attrs":{}}],"attrs":{}},{"type":"text","text":"做我的父小部件,然后在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Card","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件中添加了一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Column","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件,以稍微提升日历。然后,我在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件中添加了小","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Card","attrs":{}}],"attrs":{}},{"type":"text","text":"部件作为其子部件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"SingleChildScrollView(\n child: Column(\n children: [\n Card(\n margin: const EdgeInsets.all(8.0),\n elevation: 5.0,\n shape: const RoundedRectangleBorder(\n borderRadius: BorderRadius.all(\n Radius.circular(10),\n ),\n side: BorderSide( color: AppColors.blackCoffee, width: 2.0),\n ),\n child: TableCalendar(\n // today's date\n focusedDay: _focusedCalendarDate,\n // earliest possible date\n firstDay: _initialCalendarDate,\n // latest allowed date\n lastDay: _lastCalendarDate, \n // default view when displayed\n calendarFormat: CalendarFormat.month, \n // default is Saturday & Sunday but can be set to any day.\n // instead of day, a number can be mentioned as well.\n weekendDays: const [DateTime.sunday, 6],\n // default is Sunday but can be changed according to locale\n startingDayOfWeek: StartingDayOfWeek.monday,\n // height between the day row and 1st date row, default is 16.0\n daysOfWeekHeight: 40.0,\n // height between the date rows, default is 52.0\n rowHeight: 60.0,\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的代码正在使用一些默认值和一些根据区域设置进行自定义来设置将在移动屏幕上显示的日历。我在每个属性之前添加了注释以了解它的作用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我知道","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件的类文件中已经给出了解释,但有时用更简单的术语更容易理解属性。我习惯于阅读所有内容,理解它,然后我尝试为我的读者简化,以便他们在实现代码之前不必遍历每一行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6c/6cced7d7c489e8e93de631640211e113.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 2 步: ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"的样式","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"好的,所以还有 3 个部分来设计表格日历。首先是标题,我们有月份的名称和一个按钮,可以在周视图和月视图之间切换。左右箭头在月份之间滚动。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根据应用程序的主题,您可以自定义所有内容,以便日历的外观和感觉,基本上是日历的整个 UI,与您的应用程序的 UI 相匹配。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再次将代码拆分为 3 部分:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"codeinline","content":[{"type":"text","text":"headerStyle","attrs":{}}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日历标题样式\nheaderStyle: const HeaderStyle(\n titleTextStyle:\n TextStyle(color: AppColors.babyPowder, fontSize: 20.0),\n decoration: BoxDecoration(\n color: AppColors.eggPlant,\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10),\n topRight: Radius.circular(10))),\n formatButtonTextStyle:\n TextStyle(color: AppColors.ultraRed, fontSize: 16.0),\n formatButtonDecoration: BoxDecoration(\n color: AppColors.babyPowder,\n borderRadius: BorderRadius.all(\n Radius.circular(5.0),\n ), ),\n leftChevronIcon: Icon(\n Icons.chevron_left,\n color: AppColors.babyPowder,\n size: 28,\n ),\n rightChevronIcon: Icon(\n Icons.chevron_right,\n color: AppColors.babyPowder,\n size: 28,\n ),\n),\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/9449f4954a19ed08d3bca12c3878bfa6.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"标题下方的样式天数","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这里,您可以为周末、工作日和假期设置不同的颜色(如果您已设置):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日历日样式\ndaysOfWeekStyle: const DaysOfWeekStyle(\n // Weekend days color (Sat,Sun)\n weekendStyle: TextStyle(color: AppColors.ultraRed),\n),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的代码中,我为我在实现","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件时最初设置的周末添加了颜色。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3dd8734b73b187c263d00c8663ac3d30.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"日期样式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"您可以在此处为特定的周末日期或假期日期添加颜色。此外,可以自定义当前日期和所选日期的突出显示颜色。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日历日期样式\ncalendarStyle: const CalendarStyle(\n // Weekend dates color (Sat & Sun Column)\n weekendTextStyle: TextStyle(color: AppColors.ultraRed),\n // highlighted color for today\n todayDecoration: BoxDecoration(\n color: AppColors.eggPlant,\n shape: BoxShape.circle,\n ),\n // highlighted color for selected day\n selectedDecoration: BoxDecoration(\n color: AppColors.blackCoffee,\n shape: BoxShape.circle,\n ),\n),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/93/9370ec53f872903d6662bd255dd7a7e6.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来的代码块是从","attrs":{}},{"type":"link","attrs":{"href":"https://pub.dev/packages/table_calendar","title":"","type":null},"content":[{"type":"text","text":"所提供的官方文件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalender","attrs":{}}],"attrs":{}}]},{"type":"text","text":"。这是实现所选日期的默认方式。此代码根据上述自定义颜色突出显示当前日期和选定日期。没有更好的方法可以做到这一点,建议","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"selectedDayPredicate: (currentSelectedDate) {\n // as per the documentation 'selectedDayPredicate' needs to determine current selected day.\n return (isSameDay(\n _selectedCalendarDate!, currentSelectedDate));\n},\nonDaySelected: (selectedDay, focusedDay) {\n // as per the documentation\n if (!isSameDay(_selectedCalendarDate, selectedDay)) {\n setState(() {\n _selectedCalendarDate = selectedDay;\n _focusedCalendarDate = focusedDay;\n });\n }\n},\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 3 步:将事件添加到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我们已经完成了初始化","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"并将其风格化以匹配我们的 UI。剩下的唯一事情就是将事件添加到我们的日历中,这是一项重要功能。没有它,我们的日历只是一份硬拷贝,我们保存在家里或冰箱上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,我们中的许多人倾向于在日历上贴上便利贴来指示整个月、一周甚至一天的关键事件。在我们的手机上,我们可以将提醒或事件添加到我们的默认日历应用程序中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我创建了一个模型类,命名","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"并初始化了两个 String 变量","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventTitle","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventDescp","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"class MyEvents {\n final String eventTitle;\n final String eventDescp;\n\n MyEvents({required this.eventTitle, required this.eventDescp});\n\n @override\n String toString() => eventTitle;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我们的CustomCalendarTable","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"文件中,我添加了两个","attrs":{}}],"attrs":{}},{"type":"text","text":"TextEditingController","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"s、a和一个map,我们将在其中保存我们的事件列表并将其应用于TableCalandar 中的属性:","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"final titleController = TextEditingController();\nfinal descpController = TextEditingController();\n\nlate Map> mySelectedEvents;\n\n@override\nvoid initState() {\n selectedCalendarDate = _focusedCalendarDate;\n mySelectedEvents = {};\n super.initState();\n}\n\n@override\nvoid dispose() {\n titleController.dispose();\n descpController.dispose();\n super.dispose();\n}\n\nList _listOfDayEvents(DateTime dateTime) {\n return mySelectedEvents[dateTime] ?? [];\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我向我们添加了一个 fab 按钮,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Scaffold","attrs":{}}],"attrs":{}},{"type":"text","text":"单击 fab 按钮时,将出现一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":" ,用户将在其中输入事件标题和事件描述。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"单击 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Add","attrs":{}}],"attrs":{}},{"type":"text","text":"按钮后出现","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":",将在日历下添加一个事件,并在添加事件的日期看到一个小彩色圆点。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我还添加了一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SnackBar","attrs":{}}],"attrs":{}},{"type":"text","text":"以防用户没有在标题文本字段或描述文本字段中输入任何内容。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SnackBar","attrs":{}}],"attrs":{}},{"type":"text","text":"将弹出一条请输入标题和描述的消息/","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果用户输入了标题和描述,则在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"setState","attrs":{}}],"attrs":{}},{"type":"text","text":"方法中检查所选事件的列表是否不为空,然后我们将标题和描述添加到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"模型类并创建","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":".","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加事件后,我们将清除","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Controller","attrs":{}}],"attrs":{}},{"type":"text","text":"s 并关闭","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"_showAddEventDialog() async {\n await showDialog(\n context: context,\n builder: (context) => AlertDialog(\n title: const Text('New Event'),\n content: Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n mainAxisSize: MainAxisSize.min,\n children: [\n buildTextField(\n controller: titleController, hint: 'Enter Title'),\n const SizedBox(\n height: 20.0,\n ),\n buildTextField(\n controller: descpController, hint: 'Enter Description'),\n ], ),\n actions: [\n TextButton(\n onPressed: () => Navigator.pop(context),\n child: const Text('Cancel'),),\n TextButton(\n onPressed: () {\n if (titleController.text.isEmpty &&\n descpController.text.isEmpty) {\n ScaffoldMessenger.of(context).showSnackBar(\n const SnackBar(\n content: Text('Please enter title & description'),\n duration: Duration(seconds: 3),\n ), );\n //Navigator.pop(context);\n return;\n } else {\n setState(() {\n if (mySelectedEvents[selectedCalendarDate] != null) {\n mySelectedEvents[selectedCalendarDate]?.add(MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text));\n } else {\n mySelectedEvents[selectedCalendarDate!] = [\n MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text)\n ]; } });\n\n titleController.clear();\n descpController.clear();\n\n Navigator.pop(context);\n return;\n }\n },\n child: const Text('Add'),\n ),\n ],\n ));}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已经构建了一个自定义文本字段,该字段已在以下内容中初始化","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当我添加小部件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventLoader","attrs":{}}],"attrs":{}},{"type":"text","text":"下的属性并向其","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"添加","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_listofDayEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"方法时,一切都汇集在一起","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 需要添加这个属性来显示事件\neventLoader: _listOfDayEvents,\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就是这样,我们已经成功地实现了将事件添加到日历日期并将其显示在我们的应用程序中的日历下的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我在本文前面提到的,有一些优秀的日历库可用,例如 flutter_calendar_carousel 和 syncfusion_flutter_calendar。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的基本实现保持不变。甚至属性和自定义也与我","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"在本文中提到的非常相似。尽管属性的名称不同,但功能保持不变。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我试图包含尽可能多的细节,以帮助希望在其应用程序中集成日历的任何人,但正如我经常说的,发现则去实验,这一直是我的座右铭。因此,请使用代码,如果您需要更多关于这方面的信息,可以关注我。非常感谢!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"最后附上完整代码","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"class CustomTableCalendar extends StatefulWidget {\n const CustomTableCalendar({Key? key}) : super(key: key);\n\n @override\n _CustomTableCalendarState createState() => _CustomTableCalendarState();\n}\n\nclass _CustomTableCalendarState extends State {\n final todaysDate = DateTime.now();\n var _focusedCalendarDate = DateTime.now();\n final _initialCalendarDate = DateTime(2000);\n final _lastCalendarDate = DateTime(2050);\n DateTime? selectedCalendarDate;\n final titleController = TextEditingController();\n final descpController = TextEditingController();\n\n late Map> mySelectedEvents;\n\n @override\n void initState() {\n selectedCalendarDate = _focusedCalendarDate;\n mySelectedEvents = {};\n super.initState();\n }\n\n @override\n void dispose() {\n titleController.dispose();\n descpController.dispose();\n super.dispose();\n }\n\n List _listOfDayEvents(DateTime dateTime) {\n return mySelectedEvents[dateTime] ?? [];\n }\n\n _showAddEventDialog() async {\n await showDialog(\n context: context,\n builder: (context) => AlertDialog(\n title: const Text('New Event'),\n content: Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n mainAxisSize: MainAxisSize.min,\n children: [\n buildTextField(\n controller: titleController, hint: 'Enter Title'),\n const SizedBox(\n height: 20.0,\n ),\n buildTextField(\n controller: descpController, hint: 'Enter Description'),\n ],\n ),\n actions: [\n TextButton(\n onPressed: () => Navigator.pop(context),\n child: const Text('Cancel'),\n ),\n TextButton(\n onPressed: () {\n if (titleController.text.isEmpty &&\n descpController.text.isEmpty) {\n ScaffoldMessenger.of(context).showSnackBar(\n const SnackBar(\n content: Text('Please enter title & description'),\n duration: Duration(seconds: 3),\n ),\n );\n //Navigator.pop(context);\n return;\n } else {\n setState(() {\n if (mySelectedEvents[selectedCalendarDate] != null) {\n mySelectedEvents[selectedCalendarDate]?.add(MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text));\n } else {\n mySelectedEvents[selectedCalendarDate!] = [\n MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text)\n ];\n }\n });\n\n titleController.clear();\n descpController.clear();\n\n Navigator.pop(context);\n return;\n }\n },\n child: const Text('Add'),\n ),\n ],\n ));\n }\n\n Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n }\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: const Text('Custom Calendar'),\n ),\n floatingActionButton: FloatingActionButton.extended(\n onPressed: () => _showAddEventDialog(),\n label: const Text('Add Event'),\n ),\n body: SingleChildScrollView(\n child: Column(\n children: [\n Card(\n margin: const EdgeInsets.all(8.0),\n elevation: 5.0,\n shape: const RoundedRectangleBorder(\n borderRadius: BorderRadius.all(\n Radius.circular(10),\n ),\n side: BorderSide(color: AppColors.blackCoffee, width: 2.0),\n ),\n child: TableCalendar(\n focusedDay: _focusedCalendarDate,\n // today's date\n firstDay: _initialCalendarDate,\n // earliest possible date\n lastDay: _lastCalendarDate,\n // latest allowed date\n calendarFormat: CalendarFormat.month,\n // default view when displayed\n // default is Saturday & Sunday but can be set to any day.\n // instead of day number can be mentioned as well.\n weekendDays: const [DateTime.sunday, 6],\n // default is Sunday but can be changed according to locale\n startingDayOfWeek: StartingDayOfWeek.monday,\n // height between the day row and 1st date row, default is 16.0\n daysOfWeekHeight: 40.0,\n // height between the date rows, default is 52.0\n rowHeight: 60.0,\n // this property needs to be added if we want to show events\n eventLoader: _listOfDayEvents,\n // Calendar Header Styling\n headerStyle: const HeaderStyle(\n titleTextStyle:\n TextStyle(color: AppColors.babyPowder, fontSize: 20.0),\n decoration: BoxDecoration(\n color: AppColors.eggPlant,\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10),\n topRight: Radius.circular(10))),\n formatButtonTextStyle:\n TextStyle(color: AppColors.ultraRed, fontSize: 16.0),\n formatButtonDecoration: BoxDecoration(\n color: AppColors.babyPowder,\n borderRadius: BorderRadius.all(\n Radius.circular(5.0),\n ),\n ),\n leftChevronIcon: Icon(\n Icons.chevron_left,\n color: AppColors.babyPowder,\n size: 28,\n ),\n rightChevronIcon: Icon(\n Icons.chevron_right,\n color: AppColors.babyPowder,\n size: 28,\n ),\n ),\n // Calendar Days Styling\n daysOfWeekStyle: const DaysOfWeekStyle(\n // Weekend days color (Sat,Sun)\n weekendStyle: TextStyle(color: AppColors.ultraRed),\n ),\n // Calendar Dates styling\n calendarStyle: const CalendarStyle(\n // Weekend dates color (Sat & Sun Column)\n weekendTextStyle: TextStyle(color: AppColors.ultraRed),\n // highlighted color for today\n todayDecoration: BoxDecoration(\n color: AppColors.eggPlant,\n shape: BoxShape.circle,\n ),\n // highlighted color for selected day\n selectedDecoration: BoxDecoration(\n color: AppColors.blackCoffee,\n shape: BoxShape.circle,\n ),\n markerDecoration: BoxDecoration(\n color: AppColors.ultraRed, shape: BoxShape.circle),\n ),\n selectedDayPredicate: (currentSelectedDate) {\n // as per the documentation 'selectedDayPredicate' needs to determine\n // current selected day\n return (isSameDay(\n selectedCalendarDate!, currentSelectedDate));\n },\n onDaySelected: (selectedDay, focusedDay) {\n // as per the documentation\n if (!isSameDay(selectedCalendarDate, selectedDay)) {\n setState(() {\n selectedCalendarDate = selectedDay;\n _focusedCalendarDate = focusedDay;\n });\n }\n },\n ),\n ),\n ..._listOfDayEvents(selectedCalendarDate!).map(\n (myEvents) => ListTile(\n leading: const Icon(\n Icons.done,\n color: AppColors.eggPlant,\n ),\n title: Padding(\n padding: const EdgeInsets.only(bottom: 8.0),\n child: Text('Event Title: ${myEvents.eventTitle}'),\n ),\n subtitle: Text('Description: ${myEvents.eventDescp}'),\n ),\n ),\n ],\n ),\n ),\n );\n }\n}\nclass MyEvents {\n final String eventTitle;\n final String eventDescp;\n\n MyEvents({required this.eventTitle, required this.eventDescp});\n\n @override\n String toString() => eventTitle;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章