flutter 手写日历组件

先看效果

直接上代码

calendar_popup_view.dart

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import 'custom_calendar.dart';
import 'hotel_app_theme.dart';

class CalendarPopupView extends StatefulWidget {
  const CalendarPopupView({
    required this.initialStartDate,
    required this.initialEndDate,
    required this.onApplyClick,
    required this.onCancelClick,
    required this.state,
    this.minimumDate,
    this.maximumDate,
    this.barrierDismissible = true,
    super.key,
  });

  final DateTime? minimumDate;
  final DateTime? maximumDate;
  final bool barrierDismissible;
  final DateTime initialStartDate;
  final DateTime initialEndDate;
  final Function(DateTime, DateTime) onApplyClick;

  final Function onCancelClick;
  final Function state;
  @override
  State<CalendarPopupView> createState() => _CalendarPopupViewState();
}

class _CalendarPopupViewState extends State<CalendarPopupView>
    with TickerProviderStateMixin {
  late DateTime startDate;
  late DateTime endDate;
  late final AnimationController animationController;
  late DateTime curSelectStartData;
  late DateTime curSelectEndData;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
        duration: const Duration(milliseconds: 400), vsync: this);
    startDate = widget.initialStartDate;
    endDate = widget.initialEndDate;
    curSelectStartData = startDate;
    curSelectEndData = endDate;
    animationController.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: animationController,
      builder: (BuildContext context, _) {
        return AnimatedOpacity(
          duration: const Duration(milliseconds: 100),
          opacity: animationController.value,
          child: InkWell(
            splashColor: Colors.transparent,
            focusColor: Colors.transparent,
            highlightColor: Colors.transparent,
            hoverColor: Colors.transparent,
            onTap: () {
              if (widget.barrierDismissible) {
                Navigator.pop(context);
              }
            },
            child: Container(
              decoration: BoxDecoration(
                color: HotelAppTheme.buildLightTheme().colorScheme.background,
                borderRadius: const BorderRadius.all(Radius.circular(24.0)),
                boxShadow: <BoxShadow>[
                  BoxShadow(
                      color: Colors.grey.withOpacity(0.2),
                      offset: const Offset(4, 4),
                      blurRadius: 8.0),
                ],
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Row(
                    children: <Widget>[
                      Expanded(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Text(
                              'From',
                              textAlign: TextAlign.left,
                              style: grayTitle(),
                            ),
                            const SizedBox(
                              height: 4,
                            ),
                            Text(
                              DateFormat('EEE, dd MMM')
                                  .format(curSelectStartData),
                              style:
                                  curSelectStartData == widget.initialStartDate
                                      ? grayTime()
                                      : primaryTime(),
                            ),
                          ],
                        ),
                      ),
                      Container(
                        height: 74,
                        width: 1,
                        color: HotelAppTheme.buildLightTheme().dividerColor,
                      ),
                      Expanded(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Text(
                              'To',
                              style: grayTitle(),
                            ),
                            const SizedBox(height: 4),
                            Text(
                              DateFormat('EEE, dd MMM')
                                  .format(curSelectEndData),
                              style: curSelectEndData == widget.initialEndDate
                                  ? grayTime()
                                  : primaryTime(),
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                  const Divider(height: 1),
                  CustomCalendarView(
                      minimumDate: widget.minimumDate,
                      maximumDate: widget.maximumDate,
                      initialEndDate: widget.initialEndDate,
                      initialStartDate: widget.initialStartDate,
                      startEndDateChange:
                          (DateTime startDateData, DateTime endDateData) {
                        if (mounted) {
                          setState(() {
                            startDate = startDateData;
                            endDate = endDateData;
                            curSelectStartData = startDateData;
                            curSelectEndData = endDateData;
                          });
                          toUpdateState();
                        }
                      },
                      endDateChange: (DateTime endData) {
                        print("endDateChange");
                        setState(() {
                          endDate = endData;
                          curSelectEndData = endData;
                        });
                        toUpdateState();
                      },
                      startDateChange: (DateTime startData) {
                        print("startDateChange");
                        setState(() {
                          startDate = startData;
                          curSelectStartData = startData;
                        });
                        toUpdateState();
                      }),
                  Padding(
                    padding: const EdgeInsets.only(
                        left: 16, right: 16, bottom: 16, top: 8),
                    child: Container(
                      height: 48,
                      decoration: BoxDecoration(
                        color: HotelAppTheme.buildLightTheme().primaryColor,
                        borderRadius:
                            const BorderRadius.all(Radius.circular(24.0)),
                        boxShadow: <BoxShadow>[
                          BoxShadow(
                            color: Colors.grey.withOpacity(0.6),
                            blurRadius: 8,
                            offset: const Offset(4, 4),
                          ),
                        ],
                      ),
                      child: Material(
                        color: Colors.transparent,
                        child: InkWell(
                          borderRadius:
                              const BorderRadius.all(Radius.circular(24.0)),
                          highlightColor: Colors.transparent,
                          onTap: () {
                            try {
                              // animationController?.reverse().then((f) {

                              // });
                              widget.onApplyClick(startDate, endDate);
                              Navigator.pop(context);
                            } catch (_) {}
                          },
                          child: const Center(
                            child: Text(
                              'Apply',
                              style: TextStyle(
                                  fontWeight: FontWeight.w500,
                                  fontSize: 18,
                                  color: Colors.white),
                            ),
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      },
    );
  }

  toUpdateState() {
    widget.state(() {});
  }

  TextStyle grayTitle() {
    return const TextStyle(
      fontWeight: FontWeight.w100,
      fontSize: 16,
      color: Color(0xff676970),
    );
  }

  TextStyle grayTime() {
    return const TextStyle(
      fontWeight: FontWeight.bold,
      fontSize: 16,
      color: Color(0xff45474D),
    );
  }

  TextStyle primaryTime() {
    return TextStyle(
      fontWeight: FontWeight.bold,
      fontSize: 16,
      color: HotelAppTheme.buildLightTheme().primaryColorDark,
    );
  }
}

custom_calendar.dart

Dart 复制代码
import 'package:app/common/util/k_date_util.dart';
import 'package:app/common/util/k_log_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:intl/intl.dart';

import 'hotel_app_theme.dart';

class CustomCalendarView extends StatefulWidget {
  const CustomCalendarView({
    required this.initialStartDate,
    required this.initialEndDate,
    required this.startEndDateChange,
    required this.startDateChange,
    required this.endDateChange,
    this.minimumDate,
    this.maximumDate,
    super.key,
  });

  final DateTime? minimumDate;
  final DateTime? maximumDate;
  final DateTime initialStartDate;
  final DateTime initialEndDate;

  final Function(DateTime, DateTime) startEndDateChange;
  final Function(DateTime) startDateChange;
  final Function(DateTime) endDateChange;
  @override
  State<CustomCalendarView> createState() => _CustomCalendarViewState();
}

class _CustomCalendarViewState extends State<CustomCalendarView> {
  List<DateTime> dateList = <DateTime>[];
  DateTime currentMonthDate = DateTime.now();
  DateTime? startDate;
  DateTime? endDate;

  @override
  void initState() {
    super.initState();
    setListOfDate(currentMonthDate);
    startDate = widget.initialStartDate;
    endDate = widget.initialEndDate;
  }

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

  void setListOfDate(DateTime monthDate) {
    dateList.clear();
    final DateTime newDate = DateTime(monthDate.year, monthDate.month, 0);
    int previousMothDay = 0;
    if (newDate.weekday < 7) {
      previousMothDay = newDate.weekday;
      for (int i = 1; i <= previousMothDay; i++) {
        dateList.add(newDate.subtract(Duration(days: previousMothDay - i)));
      }
    }
    for (int i = 0; i < (42 - previousMothDay); i++) {
      dateList.add(newDate.add(Duration(days: i + 1)));
    }
    // if (dateList[dateList.length - 7].month != monthDate.month) {
    //   dateList.removeRange(dateList.length - 7, dateList.length);
    // }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          Padding(
            padding:
                const EdgeInsets.only(left: 8.0, right: 8.0, top: 4, bottom: 4),
            child: Row(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    height: 38,
                    width: 38,
                    decoration: BoxDecoration(
                      borderRadius:
                          const BorderRadius.all(Radius.circular(24.0)),
                      border: Border.all(
                        color: HotelAppTheme.buildLightTheme().dividerColor,
                      ),
                    ),
                    child: InkWell(
                      borderRadius:
                          const BorderRadius.all(Radius.circular(24.0)),
                      onTap: () {
                        if (mounted) {
                          setState(() {
                            if (getCurMonthIsInMinMaxRange(DateTime(
                                currentMonthDate.year,
                                currentMonthDate.month,
                                0))) {
                              currentMonthDate = DateTime(currentMonthDate.year,
                                  currentMonthDate.month, 0);
                              setListOfDate(currentMonthDate);
                            }
                          });
                        }
                      },
                      child: const Icon(
                        Icons.keyboard_arrow_left,
                        color: Colors.grey,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  child: Center(
                    child: Text(
                      DateFormat('MMMM, yyyy').format(currentMonthDate),
                      style: TextStyle(
                        fontWeight: FontWeight.w500,
                        fontSize: 15.sp,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    height: 38,
                    width: 38,
                    decoration: BoxDecoration(
                      borderRadius:
                          const BorderRadius.all(Radius.circular(24.0)),
                      border: Border.all(
                        color: HotelAppTheme.buildLightTheme().dividerColor,
                      ),
                    ),
                    child: InkWell(
                      borderRadius:
                          const BorderRadius.all(Radius.circular(24.0)),
                      onTap: () {
                        if (mounted) {
                          setState(() {
                            if (getCurMonthIsInMinMaxRange(DateTime(
                                currentMonthDate.year,
                                currentMonthDate.month + 2,
                                0))) {
                              currentMonthDate = DateTime(currentMonthDate.year,
                                  currentMonthDate.month + 2, 0);
                              setListOfDate(currentMonthDate);
                            }
                          });
                        }
                      },
                      child: const Icon(
                        Icons.keyboard_arrow_right,
                        color: Colors.grey,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(right: 8, left: 8, bottom: 8),
            child: Row(
              children: getDaysNameUI(),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(right: 8, left: 8),
            child: Column(
              children: getDaysNoUI(),
            ),
          ),
        ],
      ),
    );
  }

  List<Widget> getDaysNameUI() {
    final List<Widget> listUI = <Widget>[];
    final weekendList = [5, 6];
    for (int i = 0; i < 7; i++) {
      listUI.add(
        Expanded(
          child: Center(
            child: Text(
              DateFormat('EEE').format(dateList[i]),
              style: TextStyle(
                fontSize: 14.sp,
                fontWeight: FontWeight.w500,
                color: weekendList.contains(i)
                    ? HotelAppTheme.buildLightTheme().primaryColorDark
                    : Colors.white,
              ),
            ),
          ),
        ),
      );
    }
    return listUI;
  }

  List<Widget> getDaysNoUI() {
    final List<Widget> noList = <Widget>[];
    int count = 0;
    for (int i = 0; i < dateList.length / 7; i++) {
      final List<Widget> listUI = <Widget>[];
      for (int i = 0; i < 7; i++) {
        final DateTime date = dateList[count];
        listUI.add(
          Expanded(
            child: AspectRatio(
              aspectRatio: 1.0,
              child: Stack(
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.only(top: 3, bottom: 3),
                    child: Padding(
                      padding: EdgeInsets.only(
                          top: 0,
                          bottom: 0,
                          left: isStartDateRadius(date) ? 4 : 0,
                          right: isEndDateRadius(date) ? 4 : 0),
                      child: Container(
                        decoration: BoxDecoration(
                          color: startDate != null && endDate != null
                              ? getIsItStartAndEndDate(date) ||
                                      getIsInRange(date)
                                  ? HotelAppTheme.buildLightTheme()
                                      .primaryColorDark
                                      .withOpacity(0.1)
                                  : Colors.transparent
                              : Colors.transparent,
                          borderRadius: BorderRadius.only(
                            bottomLeft: isStartDateRadius(date)
                                ? Radius.circular(15.r)
                                : const Radius.circular(0.0),
                            topLeft: isStartDateRadius(date)
                                ? Radius.circular(15.r)
                                : const Radius.circular(0.0),
                            topRight: isEndDateRadius(date)
                                ? Radius.circular(15.r)
                                : const Radius.circular(0.0),
                            bottomRight: isEndDateRadius(date)
                                ? Radius.circular(15.r)
                                : const Radius.circular(0.0),
                          ),
                        ),
                      ),
                    ),
                  ),
                  InkWell(
                    borderRadius: const BorderRadius.all(Radius.circular(32.0)),
                    onTap: () {
                      if (currentMonthDate.month == date.month) {
                        final DateTime? minimumDate = widget.minimumDate;
                        final DateTime? maximumDate = widget.maximumDate;
                        if (minimumDate != null && maximumDate != null) {
                          final DateTime newminimumDate = DateTime(
                              minimumDate.year,
                              minimumDate.month,
                              minimumDate.day - 1);
                          final DateTime newmaximumDate = DateTime(
                              maximumDate.year,
                              maximumDate.month,
                              maximumDate.day + 1);
                          if (date.isAfter(newminimumDate) &&
                              date.isBefore(newmaximumDate)) {
                            onDateClick(date);
                          }
                        } else if (minimumDate != null) {
                          final DateTime newminimumDate = DateTime(
                              minimumDate.year,
                              minimumDate.month,
                              minimumDate.day - 1);
                          if (date.isAfter(newminimumDate)) {
                            onDateClick(date);
                          }
                        } else if (maximumDate != null) {
                          final DateTime newmaximumDate = DateTime(
                              maximumDate.year,
                              maximumDate.month,
                              maximumDate.day + 1);
                          if (date.isBefore(newmaximumDate)) {
                            onDateClick(date);
                          }
                        } else {
                          onDateClick(date);
                        }
                      }
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(2),
                      child: Container(
                        decoration: BoxDecoration(
                          color: getIsItStartAndEndDate(date)
                              ? HotelAppTheme.buildLightTheme().primaryColorDark
                              : Colors.transparent,
                          borderRadius: getStartOrEndPoint(date),
                          // border: Border.all(
                          //   color: getIsItStartAndEndDate(date)
                          //       ? Colors.white
                          //       : Colors.transparent,
                          //   width: 2,
                          // ),
                          // boxShadow: getIsItStartAndEndDate(date)
                          //     ? <BoxShadow>[
                          //         BoxShadow(
                          //             color: Colors.grey.withOpacity(0.6),
                          //             blurRadius: 4),
                          //       ]
                          //     : null,
                        ),
                        child: Center(
                          child: Text(
                            '${date.day}',
                            style: ceilStyle(date),
                          ),
                        ),
                      ),
                    ),
                  ),
                  Positioned(
                    bottom: 9,
                    right: 0,
                    left: 0,
                    child: Container(
                      height: 6,
                      width: 6,
                      decoration: BoxDecoration(
                          color: DateTime.now().day == date.day &&
                                  DateTime.now().month == date.month &&
                                  DateTime.now().year == date.year
                              ? getIsInRange(date)
                                  ? Colors.white
                                  : HotelAppTheme.buildLightTheme()
                                      .primaryColorDark
                              : Colors.transparent,
                          shape: BoxShape.circle),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
        count += 1;
      }
      noList.add(Row(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: listUI,
      ));
    }
    return noList;
  }

  BorderRadius getStartOrEndPoint(DateTime date) {
    KLogUtil.log([startDate, endDate, KDateUtils.isSome(startDate, endDate)]);
    if (startDate.toString() == endDate.toString()) {
      return BorderRadius.all(
        Radius.circular(15.r),
      );
    } else if (getIsItStart(date)) {
      return BorderRadius.only(
        topLeft: Radius.circular(15.r),
        bottomLeft: Radius.circular(15.r),
      );
    } else {
      return BorderRadius.only(
        topRight: Radius.circular(15.r),
        bottomRight: Radius.circular(15.r),
      );
    }
  }

  // 日期每一格的样式
  TextStyle ceilStyle(DateTime date) {
    // 不能选择日期的样式
    if (!getIsInMinMaxRange(date)) {
      return TextStyle(
        color: Colors.grey.withOpacity(0.6),
        fontSize: MediaQuery.of(context).size.width > 360 ? 14.sp : 16.sp,
        fontWeight:
            getIsItStartAndEndDate(date) ? FontWeight.bold : FontWeight.normal,
      );
    }
    return TextStyle(
        color: getIsItStartAndEndDate(date) || getIsInRange(date)
            ? Colors.white
            : currentMonthDate.month == date.month
                ? Colors.white
                : Colors.grey.withOpacity(0.6),
        fontSize: MediaQuery.of(context).size.width > 360 ? 14.sp : 16.sp,
        fontWeight:
            getIsItStartAndEndDate(date) ? FontWeight.bold : FontWeight.normal);
  }

  bool getIsInMinMaxRange(DateTime date) {
    if (widget.minimumDate != null && widget.maximumDate != null) {
      if (date.isAfter(widget.minimumDate!) &&
          date.isBefore(widget.maximumDate!)) {
        return true;
      }
    }
    return false;
  }

  // 当前月份是否在
  bool getCurMonthIsInMinMaxRange(DateTime tgtMonth) {
    if (widget.minimumDate != null && widget.maximumDate != null) {
      if (tgtMonth.isAfter(DateTime(
              widget.minimumDate!.year, widget.minimumDate!.month, 0)) &&
          tgtMonth.isBefore(DateTime(
              widget.maximumDate!.year, widget.maximumDate!.month + 2, 0))) {
        return true;
      }
    }
    return false;
  }

  bool getIsInRange(DateTime date) {
    if (startDate != null && endDate != null) {
      if (date.isAfter(startDate!) && date.isBefore(endDate!)) {
        return true;
      }
    }
    return false;
  }

  bool getIsItStartAndEndDate(DateTime date) {
    if ((startDate != null &&
            startDate!.day == date.day &&
            startDate!.month == date.month &&
            startDate!.year == date.year) ||
        (endDate != null &&
            endDate!.day == date.day &&
            endDate!.month == date.month &&
            endDate!.year == date.year)) return true;
    return false;
  }

  bool getIsItStart(DateTime date) {
    if (startDate != null &&
        startDate!.day == date.day &&
        startDate!.month == date.month &&
        startDate!.year == date.year) return true;
    return false;
  }

  bool isStartDateRadius(DateTime date) {
    if (startDate != null &&
        startDate!.day == date.day &&
        startDate!.month == date.month) {
      return true;
    } else if (date.weekday == 1) {
      return true;
    } else {
      return false;
    }
  }

  bool isEndDateRadius(DateTime date) {
    if (endDate != null &&
        endDate!.day == date.day &&
        endDate!.month == date.month) {
      return true;
    } else if (date.weekday == 7) {
      return true;
    } else {
      return false;
    }
  }

  void onDateClick(DateTime date) {
    if (startDate == null) {
      startDate = date;
    } else if (startDate != date && endDate == null) {
      endDate = date;
    } else if (startDate!.day == date.day && startDate!.month == date.month) {
      // startDate = null;
      endDate = startDate;
    } else if (endDate != null &&
        endDate!.day == date.day &&
        endDate!.month == date.month) {
      if (endDate != null) {
        startDate = endDate;
      } else {
        endDate = null;
      }
    }
    if (startDate == null && endDate != null) {
      startDate = endDate;
      endDate = null;
    }
    if (startDate != null && endDate != null) {
      if (!endDate!.isAfter(startDate!)) {
        final DateTime d = startDate!;
        startDate = endDate;
        endDate = d;
      }
      if (date.isBefore(startDate!)) {
        startDate = date;
      } else if (date.isAfter(endDate!)) {
        endDate = date;
      } else {
        final int daysToStartDate = startDate!.difference(date).inDays.abs();
        final int daysToEndDate = endDate!.difference(date).inDays.abs();
        daysToStartDate > daysToEndDate ? endDate = date : startDate = date;
      }
    }
    if (mounted) {
      setState(
        () {
          if (startDate != null && endDate != null) {
            try {
              widget.startEndDateChange(startDate!, endDate!);
            } catch (_) {}
          }
          if (startDate != null) {
            try {
              widget.startDateChange(startDate!);
            } catch (_) {}
          }
          if (endDate != null) {
            try {
              widget.endDateChange(endDate!);
            } catch (_) {}
          }
        },
      );
    }
  }
}

hotel_app_theme.dart

Dart 复制代码
import 'package:flutter/material.dart';

class HotelAppTheme {
  static TextTheme _buildTextTheme(TextTheme base) {
    const String fontName = 'WorkSans';
    return base.copyWith(
      displayLarge: base.displayLarge?.copyWith(fontFamily: fontName),
      displayMedium: base.displayMedium?.copyWith(fontFamily: fontName),
      displaySmall: base.displaySmall?.copyWith(fontFamily: fontName),
      headlineMedium: base.headlineMedium?.copyWith(fontFamily: fontName),
      headlineSmall: base.headlineSmall?.copyWith(fontFamily: fontName),
      titleLarge: base.titleLarge?.copyWith(fontFamily: fontName),
      labelLarge: base.labelLarge?.copyWith(fontFamily: fontName),
      bodySmall: base.bodySmall?.copyWith(fontFamily: fontName),
      bodyLarge: base.bodyLarge?.copyWith(fontFamily: fontName),
      bodyMedium: base.bodyMedium?.copyWith(fontFamily: fontName),
      titleMedium: base.titleMedium?.copyWith(fontFamily: fontName),
      titleSmall: base.titleSmall?.copyWith(fontFamily: fontName),
      labelSmall: base.labelSmall?.copyWith(fontFamily: fontName),
    );
  }

  static ThemeData buildLightTheme() {
    // #54D3C2
    // #54D3C2
    // #4677FF
    const Color primaryColor = Color(0xff1C1D1F);
    const Color secondaryColor = Color(0xff1C1D1F);
    const Color primaryColorDark = Color(0xff4677FF);
    final ColorScheme colorScheme = const ColorScheme.light().copyWith(
      primary: primaryColor,
      secondary: secondaryColor,
    );
    final ThemeData base = ThemeData.light();
    return base.copyWith(
      primaryColor: primaryColor,
      primaryColorDark: primaryColorDark,
      indicatorColor: Colors.white,
      splashColor: Colors.white24,
      splashFactory: InkRipple.splashFactory,
      canvasColor: Colors.white,
      // #F6F6F6
      scaffoldBackgroundColor: const Color(0xFFF6F6F6),
      buttonTheme: ButtonThemeData(
        colorScheme: colorScheme,
        textTheme: ButtonTextTheme.primary,
      ),
      textTheme: _buildTextTheme(base.textTheme),
      primaryTextTheme: _buildTextTheme(base.primaryTextTheme),
      platform: TargetPlatform.iOS,
      colorScheme: colorScheme
          .copyWith(background: const Color(0xff1C1D1F))
          // #B00020
          .copyWith(error: const Color(0xFFB00020)),
    );
  }
}

RangePicker.dart 这个文件是使用的地方 这里还使用到了 getx 的组件 从底部弹出

Dart 复制代码
// ignore_for_file: file_names

import 'package:app/gen/assets.gen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';

import 'calendar_popup_view.dart';

class RangePicker extends StatefulWidget {
  RangePicker({super.key, required this.apply});

  final Function(DateTime, DateTime) apply;
  @override
  State<RangePicker> createState() => _RangePickerState();
}

class _RangePickerState extends State<RangePicker> {
  DateTime startDate = DateTime.now().subtract(const Duration(days: 90));
  DateTime endDate = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        openCalendarPopupView(context);
      },
      child: Row(
        children: [
          Text(
            "${DateFormat('MM/dd').format(startDate)} - ${DateFormat('MM/dd').format(endDate)}",
            style: TextStyle(
              color: const Color(0xff676970),
              fontWeight: FontWeight.w500,
              fontSize: 12.sp,
            ),
          ),
          SizedBox(
            width: 4.w,
          ),
          Assets.icon.botomArrowhead.image(width: 17.w),
        ],
      ),
    );
  }

  void openCalendarPopupView(BuildContext context) {
    Get.bottomSheet(
      isScrollControlled: true,
      StatefulBuilder(builder: (context, state) {
        return CalendarPopupView(
          state: state,
          minimumDate: DateTime.now().subtract(const Duration(days: 365)),
          maximumDate: DateTime.now(),
          initialEndDate: endDate,
          initialStartDate: startDate,
          onApplyClick: (DateTime startData, DateTime endData) {
            if (mounted) {
              setState(() {
                startDate = startData;
                endDate = endData;
              });
              state(() {
                startDate = startData;
                endDate = endData;
              });
              widget.apply(startDate, endDate);
            }
          },
          onCancelClick: () {},
        );
      }),
    );
  }
}
相关推荐
brief of gali9 分钟前
记录一个奇怪的前端布局现象
前端
Json_181790144801 小时前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
风尚云网1 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
木子02042 小时前
前端VUE项目启动方式
前端·javascript·vue.js
GISer_Jing2 小时前
React核心功能详解(一)
前端·react.js·前端框架
捂月2 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
深度混淆2 小时前
实用功能,觊觎(Edge)浏览器的内置截(长)图功能
前端·edge
Smartdaili China2 小时前
如何在 Microsoft Edge 中设置代理: 快速而简单的方法
前端·爬虫·安全·microsoft·edge·社交·动态住宅代理
秦老师Q2 小时前
「Chromeg谷歌浏览器/Edge浏览器」篡改猴Tempermongkey插件的安装与使用
前端·chrome·edge
滴水可藏海2 小时前
Chrome离线安装包下载
前端·chrome