چطور یک تقویم شمسی بدون هیچ پکیجی ایجاد کنیم(فلاتر)

خب شاید به فکرتون رسیده یک تقویمی برای خودتون بنویسید و توی برنامه های خودتون استفاده کنید و اون رو طبق سلیقه خودتون شخصی سازی کنید. ولی خب به هر دلیل نمیتونید اینکار بکنید توی این مقاله من میخوام بهتون بگم چطوری میتونید یک تقویم شمسی ایجاد کنید.


اول باید بگم که این کد برای فلاتر هستش ولی خب میتونید با کمی تغییر در زبان های دیگه هم پیاده سازی کنید. من برای پایتون هم تستش کردم جواب داد.


ابتدا 3تا متغیر ایجاد میکنیم به صورت زیر


  static List<String> sortedDays = const [
    'شنبه',
    'یکشنبه',
    'دوشنبه',
    'سه شنبه',
    'چهارشنبه',
    'پنج شنبه',
    'جمعه',
  ];

کار این متغیر بر گردوندن نام هر روز هفته هستش

    static List<String> weekDay = const [
    'یکشنبه',
    'دوشنبه',
    'سه شنبه',
    'چهارشنبه',
    'پنج شنبه',
    'جمعه',
    'شنبه'
  ];

حتما میدونید که روز های هفته توی تقویم میلادی از روی یک شنبه شروع میشه پس weekDay شماره روز رو برای ما بر میگردونه


  static List<String> dayH = const [
    'ش',
    'ی',
    'د',
    'س',
    'چ',
    'پ',
    'ج',
  ]; 

خب dayH هم معلومه دیگه برای چی هستش برای نمایش نام روز بالای تقویم خودمون هستش

  
    List<Widget> _getDayHeaders() {
    final List<Widget> result = <Widget>[];
    for (String dayHader in dayH) {
      result.add(ExcludeSemantics(
        child: Center(child: Text(dayHader)),
      ));
    }
    return result;
  }

این تابع برای این هستش که هدر تقویم خودمون رو ایجاد کنیم همون حروف اول هر روز

   final List<Widget> labels = <Widget>[];

یک لیستی از نوع ویجت ایجاد میکنیم

خب حالا میایم تابعی مینویسیم که بتونیم روز های یک ماه رو حساب کنیم

خب اولین روز یک ماه از شنبه تا یک شنبه متغییر هستش بر اساس شماره هفته

  days() {
  // اینجا من اومدم به صورت static اولین روز ماه خرداد سال 1400 رو به صورت میلادی در تابع DateTime قرار دادم
  // ما در این تابع میایم تاریخ ها را بر اساس تاریخ میلادی سیستم محاسبه میکنیم
  // اگر تقویمی دم دست شما هستش میبینید که اولین روز خرداد ماه میشه 22/5/2021
  var gdate = DateTime(2021,05, 22); 
  
  // خب حالا ما بر اساس متغیری که در بالا تعریف کردیم میایم شماره روز رو میگیریم 
 // اینجا گفتم اگر شماره برار 7 بود یعنی 7 روز هفته به میلادی که میشه یکشنبه برای ما 0 رو برگردون در غییر این صورت شماره اون هفته رو به ما بده
  var daynum = gdate.weekday == 7 ? 0 : gdate.weekday;
  
 // خب حالا میایم  اندیس اون روزی که در بالا گرفتیم رو از لیست weekDay میگیریم
 var startday = sortedDays.indexOf(weekDay[daynum]);
  
  
  // حالا میایم و اون روز های که تابعش رو در بالا ایجاد کردیم برای نمایش در بالای تقویم خودمون هستش رو توی  متغیری label قرار میدیم
  labels.addAll(_getDayHeaders());
  
  // یک ایجاد میکنیم که تعداد روز های ماه رو بر گردونه
  var daysInMonth = 31;
  
  // یک متغیر برای بر گردوندن روزی که هستش(امروز)
  int today = 27;
  
  // متغییر بعدی هم روزی که توسط کاربر انتخاب شده اینجا به صورت static قرار میدیم
  int selectedDay = 28;
  
  // اینجا یک حلقه ایجاد کردیم که شرط اون برابر true هستش 
  // این حلقه تا زمانی که شمارنده کوچیکتر از تعداد روز های هفته هستش ادامه پیدا میکنه
  for (int i = 0; true; i ++) {
	// خب میایم چیکار میکنیم 
	// میگم که شمارهنده رو بگیر از اون اندیسی که به عنوان  روز شروع ماه هستش کم کن و با 1 جمعش کن
	final int day = i - startday + 1;
	// اول باید یک شرطی بنویسیم بعد از عمل بالا که اگر عدد روز ما از تعداد روز های که بالا تعیین کردیم که 31 هستش بیشتر بود از حلقه خارج شو
	if (day > daysInMonth) break;
	// شرط دوم هم برای چینش شماره روز ها هستش
	// اینجا گفتیم اگر عدد روز  کمتر از 1 بود یک ویجت خالی اضافه کن
	if (day < 1) {
		labels.add(Container());
	}else{
	// در غییر این صورت شماره اون روز رو برای ما قرار بده
	// قبل از هرچیزی باید روزی که توش هستیم رو تعیین کنیم که میشه همون امروز
	bool inToday = today == day;
	// بعد روزی که کاربر انتخاب کرده رو باید رنگی کنیم
	bool slected = day == selectedDay;
	
	// حالا میایم رنگ روز ها رو تعیین میکنیم
	// روزی که ما توش هستیم(امروز) رنگ قرمز میگیره
	// روزی که کاربر انتخاب کرده خاکستری
	// روزهای دیگه هم به رنگ آبی در میاریم
	BoxDecoration decoration = BoxDecoration(color: inToday ? Colors.red : slected ? Colors.grey : Colors.blue, shape: BoxShape.circle);
	
	
	// حالا یک ویجتی ایجاد میکنیم و شماره روز رو توی اون قرار میدیم
	Widget dayWidget = Container(
          margin: EdgeInsets.only(top: 5),
          decoration: decoration,
          child: Center(
            child: Semantics(
              label: '$day, $day',
              child: ExcludeSemantics(
                child: Text(&quot$day&quot),
              ),
            ),
          ),
        );
		
		// خب حالا میخوایم بگیم اون روزی که انتخاب شده دیگه قابل انتخاب نباشه و روز های دیگه قابل انتخاب باشند به صورت زیر عمل میکنیم
		if (!slected) {
          dayWidget = GestureDetector(
            /span>
            onTap: () {
              print(day);
              setState((){
                selectedDay = day;
              });
              },
            child: dayWidget,
          );
        }
		
		// و در نهایت ویجت رو به لیست ویجت های خودمون اضافه میکنیم
		labels.add(dayWidget);
	}
  }
}


حالا یک کلاسی از نوع SliverGridDelegate ایجاد میکنیم تا بتونید توی GridView از اون استفاده کنیم توی خاصیت childrenDelegate از GridView

const double _kDayPickerRowHeight = 32.0;
const int _kMaxDayPickerRowCount = 6;
class _DayPickerGridDelegate extends SliverGridDelegate {
  const _DayPickerGridDelegate();
  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    const int columnCount = DateTime.daysPerWeek;
    final double tileWidth = constraints.crossAxisExtent / columnCount;
    final double tileHeight = math.min(_kDayPickerRowHeight,
        constraints.viewportMainAxisExtent / (_kMaxDayPickerRowCount + 1));
    return SliverGridRegularTileLayout(
      crossAxisCount: columnCount,
      mainAxisStride: tileHeight,
      crossAxisStride: tileWidth,
      childMainAxisExtent: tileHeight,
      childCrossAxisExtent: tileWidth,
      reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
    );
  }
  @override
  bool shouldRelayout(_DayPickerGridDelegate oldDelegate) => false;
}



حالا میرسیم به اصل کاری و نمایش تقویمی که ایجاد کردیمم در گرید ویو

const _DayPickerGridDelegate _kDayPickerGridDelegate = _DayPickerGridDelegate();
Directionality(
          textDirection: TextDirection.rtl,
          child: Center(
              child: Container(
                  width: 250,
                  child: GridView.custom(
                    gridDelegate: _kDayPickerGridDelegate,
                    childrenDelegate: SliverChildListDelegate(labels,
                        addRepaintBoundaries: false),
                  ))))

کار ما هم تموم شده

شما میتونید این تقویم رو بهینه تر کنید مرتبت کنید و ازش استفاده کنید

نکته خیلی مهم یادتون باشه که برای گرفتن اولین روز هر ماه میتونید از پکیج های موجود که تاریخ شمسی رو به میلادی تبدیل میکنند استفاده کنید به صورت زیر

شما بهش 1400/03/01 رو میدید و به میلادی بر میگردونید که تاریخ 2021/05/22 رو میده حالا میتونید توی DateTime قرار بدید و تقویم رو ایجاد کنید

میتونید لیستی از سال ها و ماه ها رو هم ایجاد کنید و یک تقویم کاملی ایجاد کنید


خب امید وارم که این مقاله هم بدرد شما خورده باشه


کپی میکنید هم اسم جواد زبیدی زاده رو به عنوان نویسنده ذکر کنید :)