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: () {},
        );
      }),
    );
  }
}
相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
renke33643 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端