weekday_selector 0.3.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 82

weekday_selector #

A collection of Flutter widgets and classes to help you select weekdays in your apps. Perfect for recurring events, alarms.

smaho-engineering/weekday_selector

Build Status Code coverage

weekday_selector GitHub Stars Count

GIF Flutter plugin weekday_selector - Example app in action: Styles

Usage #

Example app #

You'll find the best examples in the package's /example folder on GitHub. There, you'll see examples for basic usage, style customization, and internationalization.

Basic usage #

The WeekdaySelector works well with any state management solution. This is how the typical usage with a simple stateful widget looks like:

class ExampleState extends State<ExampleWidget> {
  // We start with all days selected.
  final values = List.filled(7, true);

  @override
  Widget build(BuildContext context) {
    return WeekdaySelector(
      onChanged: (int day) {
        setState(() {
          // Use module % 7 as Sunday's index in the array is 0 and
          // DateTime.sunday constant integer value is 7.
          final index = day % 7;
          // We "flip" the value in this example, but you may also
          // perform validation, a DB write, an HTTP call or anything
          // else before you actually flip the value,
          // it's up to your app's needs.
          values[index] = !values[index];
        });
      },
      values: values,
    );
  }
}

When creating a WeekdaySelector widget, pass a List<bool> of length 7 as the values parameter. values[0] is Sunday, values[1] for Monday, and so on. The values list may also contain nulls, in that case the day will be disabled.

Implement the onChanged callback, if you want to handle user interaction. The onChanged callback will be called with the int integers matching the DateTime day constants: DateTime.monday == 1, ..., DateTime.sunday == 7: if the user taps on Wednesday, the onChanged callback will be called with 3.

Customization #

The WeekdaySelector class accepts many customization options: you can tweak the fill colors, text style, shape of the days, elevation, and more. In case you don't provide any of these values, the library will do its best to figure out a style that matches your material app's theme.

To see the list of all supported arguments, check out the API reference

Theme support

If you want to control multiple selectors' appearance, take a look at the WeekdaySelectorTheme widget.

It works exactly like other theme widgets: the descendant weekday widgets will use the theme's attributes. Arguments passed directly to the widgets override the values inherited from the theme.

Internationalization #

We aim to provide you with a widget that supports all languages. The WeekdaySelector accepts custom button texts, tooltips, the first day of the wee, and text direction RTL-LTR (see shortWeekdays, weekdays, firstDayOfWeek, textDirection arguments, respectively).

In case these parameters are omitted, English ("en ISO") will be used.

GIF Flutter plugin weekday_selector - Example app in action: Internationalization

intl

We recommend you check out the intl package's DateSymbols class, if you need to support multiple languages.

First day of week

Please keep in mind that the intl package uses 0 value as FIRSTDAYOFWEEK value for locales that start with Monday, whereas DateTime.monday is equal to 1. See dart-lang #265 . The intl package and the core Dart library days notation is inconsistent, so we decided to use the Dart core library's convention in the weekday_selector package.

Therefore, if you use the intl library to decide which day should the week start with, do not forget to add +1 to FIRSTDAYOFWEEK before you pass it to the WeekdaySelector widget:

WeekdaySelector(
  // intl package uses 0 for Monday, but DateTime uses 1 for Monday,
  // so we need to make sure the values match
  firstDayOfWeek: dateSymbols.FIRSTDAYOFWEEK + 1,
),

Community #

I gave a talk about how I developed this package at Flutter Munich. Watch the video recording here or see the slides.

Vince Varga - Developing a weekday selector widget thumbnail 1Vince Varga - Developing a weekday selector widget thumbnail 2Vince Varga - Developing a weekday selector widget thumbnail 3

0.3.0 #

Add displayedDays argument.

0.2.2 #

Revert Diagnoticable and text theme changes.

0.2.1 was not tested with the stable channel, so some changes were made to this package only worked on the dev and beta channels, but not on stable.

As the package aims to support the stable channel, those updates were reverted.

The CI/CD pipeline has been also updated so that this won't occur again: we run the tests on the stable channel.

0.2.1 #

Fix issue with WeekdaySelectorTheme. The build error was: "The superclass, 'Diagnosticable', has no unnamed constructor that takes no arguments."

Link Vince's talk about the package: YouTube video and slides.

Moaaar README improvements.

0.2.0 #

WeekdaySelector, WeekdaySelectorTheme should be ready to use and the README is also not so terrible anymore :)

0.1.2 #

Improve READMEs, documentation and the example project.

0.1.1 #

WeekdaySelector widget #

  • internationalization-friendly: change texts, directionality, and first day of the week
  • highly-customizable: customize colors, text styles, elevations, and shapes
  • sane defaults: colors are picked based on your material theme

Theming with WeekdaySelectorTheme #

The weekday selector provides its own inherited widget which you can use if you want to customize multiple weekday selectors in your subtree.

Example app, testing #

The initial version of the example app is added to the project. We improved the automated test suite, and execute checks on every commit to master to ensure good quality.

0.1.0 #

Initial release.

example/lib/main.dart

import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/date_symbols.dart';
import 'package:weekday_selector/weekday_selector.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Locale> locales = [];

  @override
  Widget build(BuildContext context) {
    const title = 'Weekday Selector Example';
    return MaterialApp(
      title: title,
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.deepPurple),
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          currentIndex: bottomSelectedIndex,
          onTap: onBottomNavigationBarTap,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text('Usage'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.flag),
              title: Text('i18n'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.ac_unit),
              title: Text('Styles'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.send),
              title: Text('Forms'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.play_circle_outline),
              title: Text('Animated'),
            ),
          ],
        ),
        appBar: AppBar(title: Text(title)),
        body: PageView(
          controller: pageController,
          onPageChanged: onPageChanged,
          children: <Widget>[
            UsageExamples(),
            I18nExamples(),
            StylesExamples(),
            FormsExamples(),
            AnimatedExamples(),
          ],
        ),
      ),
      supportedLocales: locales,
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
    );
  }

  @override
  void initState() {
    // In the example app, we want to mark every locale that the `intl` package
    // support as supported so that you can test the weekday selector quickly,
    // so we just convert intl symbols to locales, but in your app, it is
    // very likely that you would want something else.
    // Learn more about internationalization here:
    // https://flutter.dev/docs/development/accessibility-and-localization/internationalization
    locales = dateTimeSymbolMap()
        .keys
        .cast<String>()
        .map((String k) => Locale(
            k.split('_')[0], k.split('_').length > 1 ? k.split('_')[1] : null))
        .toList();
    super.initState();
  }

  final pageController = PageController(initialPage: 0, keepPage: true);

  int bottomSelectedIndex = 0;

  void onPageChanged(int index) {
    setState(() => bottomSelectedIndex = index);
  }

  void onBottomNavigationBarTap(int index) {
    bottomSelectedIndex = index;
    pageController.animateToPage(
      index,
      duration: Duration(milliseconds: 400),
      curve: Curves.ease,
    );
  }
}

class UsageExamples extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    initializeDateFormatting();
    final examples = <Widget>[
      SimpleExampleWeekendsStatic(),
      SelectedDaysUpdateExample(),
      DisabledExample(),
      DisplayedDaysExample(),
      // TODO: use with setstate
    ];
    return ListView.separated(
      padding: EdgeInsets.all(12),
      separatorBuilder: (_, __) => Divider(height: 24),
      itemBuilder: (_, index) => examples[index],
      itemCount: examples.length,
    );
  }
}

class I18nExamples extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final examples = <Widget>[
      CurrentLocaleExample(),
      CustomWeekdaysTexts(),
      ShortAndNarrowGermanExample(),
      RegionMattersSpanishExample(),
      FirstDayOfWeekExample(),
    ];
    return ListView.separated(
      padding: EdgeInsets.all(12),
      separatorBuilder: (_, __) => Divider(height: 24),
      itemBuilder: (_, index) => examples[index],
      itemCount: examples.length,
    );
  }
}

class StylesExamples extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final examples = <Widget>[
      SaneDefaultThemeExample(),
      ElevationExample(),
      SimpleShapesExample(),
      CustomShapesExample(),
      InheritedThemeExample(),
    ];
    return ListView.separated(
      padding: EdgeInsets.all(12),
      separatorBuilder: (_, __) => Divider(height: 24),
      itemBuilder: (_, index) => examples[index],
      itemCount: examples.length,
    );
  }
}

class FormsExamples extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // No forms supprt yet, but it would come here.
    final examples = <Widget>[];
    return ListView.separated(
      padding: EdgeInsets.all(12),
      separatorBuilder: (_, __) => Divider(height: 24),
      itemBuilder: (_, index) => examples[index],
      itemCount: examples.length,
    );
  }
}

class AnimatedExamples extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final examples = <Widget>[];
    return ListView.separated(
      padding: EdgeInsets.all(12),
      separatorBuilder: (_, __) => Divider(height: 24),
      itemBuilder: (_, index) => examples[index],
      itemCount: examples.length,
    );
  }
}

/// Print the integer value of the day and the day that it corresponds to in English.
///
/// It's added to the example so that you can always see and verify that the
/// code is correct.
printIntAsDay(int day) {
  print('Received integer: $day. Corresponds to day: ${intDayToEnglish(day)}');
}

String intDayToEnglish(int day) {
  if (day % 7 == DateTime.monday % 7) return 'Monday';
  if (day % 7 == DateTime.tuesday % 7) return 'Tueday';
  if (day % 7 == DateTime.wednesday % 7) return 'Wednesday';
  if (day % 7 == DateTime.thursday % 7) return 'Thursday';
  if (day % 7 == DateTime.friday % 7) return 'Friday';
  if (day % 7 == DateTime.saturday % 7) return 'Saturday';
  if (day % 7 == DateTime.sunday % 7) return 'Sunday';
  throw '🐞 This should never have happened: $day';
}

class ExampleTitle extends StatelessWidget {
  final String title;

  const ExampleTitle(this.title, {Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Text(title, style: Theme.of(context).textTheme.title),
    );
  }
}

/// [SimpleExampleWeekendsStatic] shows how to pass initial values to the
/// [WeekdaySelector] widget and demonstrates how the `onChanged`
/// callback works.
class SimpleExampleWeekendsStatic extends StatefulWidget {
  @override
  _SimpleExampleWeekendsStaticState createState() =>
      _SimpleExampleWeekendsStaticState();
}

class _SimpleExampleWeekendsStaticState
    extends State<SimpleExampleWeekendsStatic> {
  int lastTapped;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('onChanged callback'),
        Text(
            'The selected days are Saturday and Sunday. The onChanged function will be called with the index of the day that the user has tapped on.'),
        Text(
            'It is up to the user of this library to handle the taps and keep track of the changes'),
        Text(
            'In accordance with ISO 8601 a week starts with Monday, which has the value of 1. Sunday == 7'),
        Text(
          lastTapped == null
              ? 'onChanged callback was not yet called'
              : 'onChanged callback was last called with $lastTapped',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        WeekdaySelector(
          // We display the last tapped value in the example app
          onChanged: (v) {
            printIntAsDay(v);
            setState(() => lastTapped = v);
          },
          values: [
            true, // Sunday
            false, // Monday
            false, // Tuesday
            false, // Wednesday
            false, // Thursday
            false, // Friday
            true, // Saturday
          ],
        ),
      ],
    );
  }
}

class DisabledExample extends StatefulWidget {
  @override
  _DisabledExampleState createState() => _DisabledExampleState();
}

class _DisabledExampleState extends State<DisabledExample> {
  final values = <bool>[null, false, true, false, true, false, null];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Disabled days'),
        Text(
            'The package also supports disabled days. Maybe, you want to prevent users from selecting weekends. Just use "null".'),
        // Using v == true, as some values could be null!
        Text(
            'The days that are currently selected are: ${valuesToEnglishDays(values, true)}. The following days are disabled: ${valuesToEnglishDays(values, null)}'),
        WeekdaySelector(
          selectedFillColor: Colors.indigo,
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
        ),
      ],
    );
  }
}

class SelectedDaysUpdateExample extends StatefulWidget {
  @override
  _SelectedDaysUpdateExampleState createState() =>
      _SelectedDaysUpdateExampleState();
}

class _SelectedDaysUpdateExampleState extends State<SelectedDaysUpdateExample> {
  final values = List.filled(7, false);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Stateful widget with selected days'),
        Text(
            'When the user taps on a day, toggle the state! You can use stateful widgets, or any other methods for managing your state.'),
        // Using v == true, as some values could be null!
        Text(
            'The days that are currently selected are: ${valuesToEnglishDays(values, true)}.'),
        WeekdaySelector(
          selectedFillColor: Colors.indigo,
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
        ),
      ],
    );
  }
}

class DisplayedDaysExample extends StatefulWidget {
  @override
  _DisplayedDaysExampleState createState() => _DisplayedDaysExampleState();
}

class _DisplayedDaysExampleState extends State<DisplayedDaysExample> {
  final values = List.filled(7, false);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Displayed days'),
        Text(
          'You can select which days you want to display to your users. '
          'Though this makes the weekday selector more difficult to understand, '
          'for some use-cases, it can be a good option to consider: if your app '
          'lets teachers select weekdays, then maybe you do not need to display '
          'the days of the weekend?',
        ),
        Text(
          'The days that are currently selected are: '
          '${valuesToEnglishDays(values, true)}.',
        ),
        WeekdaySelector(
          // Just some days you want to display to your users.
          displayedDays: {
            DateTime.tuesday,
            DateTime.wednesday,
            DateTime.thursday,
            DateTime.friday,
            DateTime.saturday,
          },
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
        ),
      ],
    );
  }
}

String valuesToEnglishDays(List<bool> values, bool searchedValue) {
  final days = <String>[];
  for (int i = 0; i < values.length; i++) {
    final v = values[i];
    // Use v == true, as the value could be null, as well (disabled days).
    if (v == searchedValue) days.add(intDayToEnglish(i));
  }
  if (days.isEmpty) return 'NONE';
  return days.join(', ');
}

class ElevationExample extends StatefulWidget {
  @override
  _ElevationExampleState createState() => _ElevationExampleState();
}

class _ElevationExampleState extends State<ElevationExample> {
  final values = <bool>[null, false, true, false, true, false, null];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Elevation'),
        Text('We support custom elevations, too!'),
        WeekdaySelector(
          selectedFillColor: Colors.indigo.shade300,
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          selectedElevation: 15,
          elevation: 5,
          disabledElevation: 0,
          values: values,
        ),
      ],
    );
  }
}

class CurrentLocaleExample extends StatefulWidget {
  @override
  _CurrentLocaleExampleState createState() => _CurrentLocaleExampleState();
}

class _CurrentLocaleExampleState extends State<CurrentLocaleExample> {
  final values = <bool>[
    true,
    true,
    true,
    false,
    false,
    false,
    null,
  ];

  @override
  Widget build(BuildContext context) {
    // TODO: This example should be simpler!
    // TODO: it should work somewhat like this:
    // initializeDateFormatting...
    // final dateSymbols = DateFormat().dateSymbols;
    final locale = Localizations.localeOf(context);
    final DateSymbols dateSymbols = dateTimeSymbolMap()['$locale'];
    final textDirection = getTextDirection(locale);
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle(
            'Current locale: $locale (${textDirection == TextDirection.rtl ? "RTL" : "LTR"})'),
        Text(
            'The WeekdaySelector is built to support multiple languages, locales in one application.'),
        Text(
          'Just pass the WeekdaySelector the "weekdays", "shortWeekdays", and "firstDayOfWeek" parameters. '
          'You can use the intl package to get these parameters.',
        ),
        Text(
            'The selector below will automatically use your current language.\nCurrently selected: ${valuesToEnglishDays(values, true)}.\nDisabled: ${valuesToEnglishDays(values, null)}.'),
        WeekdaySelector(
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
          // intl package uses 0 for Monday, but DateTime uses 1 for Monday,
          // so we need to make sure the values match
          firstDayOfWeek: dateSymbols.FIRSTDAYOFWEEK + 1,
          shortWeekdays: dateSymbols.STANDALONENARROWWEEKDAYS,
          weekdays: dateSymbols.STANDALONEWEEKDAYS,
          textDirection: textDirection,
        ),
      ],
    );
  }
}

TextDirection getTextDirection(Locale locale) {
  // See GlobalWidgetsLocalizations
  // TODO: there must be a better way to figure out whether a locale is RTL or LTR
  const rtlLanguages = ['ar', 'fa', 'he', 'ps', 'sd', 'ur'];
  return rtlLanguages.contains(locale.languageCode)
      ? TextDirection.rtl
      : TextDirection.ltr;
}

class FirstDayOfWeekDateTime extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Text(
            'Use the "firstDayOfWeek" property to change which day is the first day of the week.'),
        Text('Starting with Monday'),
        WeekdaySelector(
          onChanged: print,
          values: valuesSaturdaySunday,
          firstDayOfWeek: DateTime.monday,
        ),
        Text('Starting with Sunday'),
        WeekdaySelector(
          onChanged: print,
          values: valuesSaturdaySunday,
          firstDayOfWeek: DateTime.sunday,
        ),
        Text('Starting with Saturday'),
        WeekdaySelector(
          onChanged: print,
          values: valuesSaturdaySunday,
          firstDayOfWeek: DateTime.saturday,
        ),
      ],
    );
  }
}

class CustomWeekdaysTexts extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Customizable day tags'),
        Text(
            'In case you need to support only a couple of languages or for some reason, or you disagree with the labels from the intl package, you can set the days to any strings. In this example, I used emojis to represent days, so for example "😎" is for Sunday.'),
        WeekdaySelector(
          // We display the last tapped value in the example app
          onChanged: printIntAsDay,
          values: valuesSaturdaySunday,
          shortWeekdays: [
            '😎', // Sunday
            '🌚', // MOONday
            'πŸ‘½', // https://en.wikipedia.org/wiki/Names_of_the_days_of_the_week
            'πŸ™‚', // I ran out of ideas...
            '🍺', // Thirst-day
            '🍻', // It's Friday, Friday, Gotta get down on Friday!
            'πŸ†“', // Everybody's lookin' forward to the weekend, weekend
          ],
        ),
      ],
    );
  }
}

class ShortAndNarrowGermanExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final DateSymbols de = dateTimeSymbolMap()['de'];
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Narrow and Short weekdays'),
        Text(
            'Use of the intl package is recommended in case you need to support multiple languages. This example is using the German "short" weekdays.'),
        WeekdaySelector(
          // We display the last tapped value in the example app
          onChanged: printIntAsDay,
          values: List.filled(7, true),
          weekdays: de.STANDALONEWEEKDAYS,
          shortWeekdays: de.STANDALONESHORTWEEKDAYS,
          firstDayOfWeek: de.FIRSTDAYOFWEEK + 1,
        ),
        Text(
            'This example is using the German "short" weekdays. Use the narrow weekdays if you want a *really short* version of the weekdays.'),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: List.filled(7, true),
          weekdays: de.STANDALONEWEEKDAYS,
          shortWeekdays: de.STANDALONENARROWWEEKDAYS,
          firstDayOfWeek: de.FIRSTDAYOFWEEK + 1,
        ),
      ],
    );
  }
}

class RegionMattersSpanishExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final DateSymbols mx = dateTimeSymbolMap()['es_MX'];
    final DateSymbols es = dateTimeSymbolMap()['es_ES'];
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Country code matters'),
        Text(
            'The same language from two different countries might use different abbreviations for the weekdays or might start the week with a different value.\n\nIn this example, we used "es_MX" and "es_ES" that correspond to Mexico and Spain. In Mexico, miΓ©rcoles is abbreviated to M, in Spain, it is X. In Mexico, weeks start with Saturday, and in Spain they start with Sunday.\n\nSaturday and Sunday are the selected days.'),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: valuesSaturdaySunday,
          weekdays: mx.STANDALONEWEEKDAYS,
          shortWeekdays: mx.STANDALONENARROWWEEKDAYS,
          firstDayOfWeek: mx.FIRSTDAYOFWEEK + 1,
        ),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: valuesSaturdaySunday,
          weekdays: es.STANDALONEWEEKDAYS,
          shortWeekdays: es.STANDALONENARROWWEEKDAYS,
          firstDayOfWeek: es.FIRSTDAYOFWEEK + 1,
        ),
      ],
    );
  }
}

final x = DateTime.monday;

const valuesSundayTuesdayThursday = <bool>[
  true,
  false,
  true,
  false,
  true,
  false,
  false,
];

const valuesSaturdaySunday = <bool>[
  // Sunday
  true,
  // Monday-Friday
  false,
  false,
  false,
  false,
  false,
  // Saturday
  true,
];

class SaneDefaultThemeExample extends StatefulWidget {
  @override
  _SaneDefaultThemeExampleState createState() =>
      _SaneDefaultThemeExampleState();
}

class _SaneDefaultThemeExampleState extends State<SaneDefaultThemeExample> {
  final values = <bool>[null, null, true, true, false, false, true];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Sane defaults'),
        Text(
            'The colors will be picked based on your current theme, so the weekday selector will match your theme without you having to set every color on your own.'),
        Text(
            'Notice how the colors of the picked days will match the material theme of your app!'),
        WeekdaySelector(
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
        ),
      ],
    );
  }
}

class SimpleShapesExample extends StatefulWidget {
  @override
  _SimpleShapesExampleState createState() => _SimpleShapesExampleState();
}

class _SimpleShapesExampleState extends State<SimpleShapesExample> {
  final values = <bool>[null, false, true, false, true, false, null];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Shapes'),
        Text(
            'You can customize the shape of the weekday selector buttons via the "shape", "selectedShape", and "disabledShape" parameters.'),
        WeekdaySelector(
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
          selectedFillColor: Colors.amber,
          selectedColor: Colors.black,
          selectedShape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(5),
          ),
          shape: RoundedRectangleBorder(
            side: BorderSide(color: Colors.red.withOpacity(0.5)),
            borderRadius: BorderRadius.circular(25),
          ),
        ),
      ],
    );
  }
}

class CustomShapesExample extends StatefulWidget {
  @override
  _CustomShapesExampleState createState() => _CustomShapesExampleState();
}

class _CustomShapesExampleState extends State<CustomShapesExample> {
  final values = <bool>[null, false, true, false, true, false, null];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('Shapes: Any ShapeBorder works'),
        Text(
            'Any "ShapeBorder" will do, and you can set the selected, enabled, and disabled shapes differently'),
        WeekdaySelector(
          onChanged: (v) {
            printIntAsDay(v);
            setState(() {
              values[v % 7] = !values[v % 7];
            });
          },
          values: values,
          selectedFillColor: Colors.amber,
          selectedTextStyle: TextStyle(
            color: Colors.red,
            fontWeight: FontWeight.bold,
          ),
          textStyle: TextStyle(color: Colors.black),
          selectedShape: BeveledRectangleBorder(
            side: BorderSide(color: Colors.black, width: 4),
            borderRadius: BorderRadius.all(Radius.circular(10)),
          ),
          shape: BeveledRectangleBorder(
            side: BorderSide(color: Colors.green, width: 2),
            borderRadius: BorderRadius.all(
              Radius.elliptical(100, 10),
            ),
          ),
          disabledShape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20),
          ),
          disabledFillColor: Colors.black45,
          disabledColor: Colors.yellowAccent,
        ),
      ],
    );
  }
}

class InheritedThemeExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WeekdaySelectorTheme(
      data: WeekdaySelectorThemeData(
        color: Colors.red,
        fillColor: Colors.white70,
        selectedFillColor: Colors.red,
        // Warning: text style overwrites this!
        selectedColor: Colors.black,
        selectedTextStyle: TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.bold,
          // Beautiful!
          decoration: TextDecoration.overline,
          decorationColor: Colors.black,
          decorationThickness: 4,
        ),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          ExampleTitle('WeekdaySelectorTheme'),
          Text(
            'The package also has its own theme widget: "WeekdaySelectorTheme". '
            'This theme is an inherited theme and you can control the appearance of all descendant weekday selectors. '
            'You can still overwrite the theme by passing values directly to the widget.',
          ),
          WeekdaySelector(
            onChanged: printIntAsDay,
            values: List.filled(7, true),
          ),
          Text(
              'You can still overwrite every value! Let\'s make it green by passing the "selectedFillColor" value!'),
          WeekdaySelector(
            onChanged: printIntAsDay,
            values: List.filled(7, true),
            // Overwrites the theme.
            selectedFillColor: Colors.green,
          ),
        ],
      ),
    );
  }
}

/// Demo how to change the first day of week.
class FirstDayOfWeekExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ExampleTitle('First day of week'),
        Text(
            'The first day of the week changes from country to country. You can use the "first day of week" parameter to configure this value.'),
        Text('If firstDayOfWeek is omitted, Monday is used.'),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: valuesSundayTuesdayThursday,
        ),
        Text('First day is Sunday:'),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: valuesSundayTuesdayThursday,
          firstDayOfWeek: DateTime.sunday,
        ),
        Text('First day is Thursday:'),
        WeekdaySelector(
          onChanged: printIntAsDay,
          values: valuesSundayTuesdayThursday,
          firstDayOfWeek: DateTime.thursday,
        ),
      ],
    );
  }
}

final monday = DateTime.monday;

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  weekday_selector: ^0.3.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:weekday_selector/weekday_selector.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
64
Health:
Code health derived from static analysis. [more]
99
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
82
Learn more about scoring.

We analyzed this package on Jun 5, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.2
  • pana: 0.13.8-dev
  • Flutter: 1.17.1

Health suggestions

Fix lib/src/weekday_selector.dart. (-1.49 points)

Analysis of lib/src/weekday_selector.dart reported 3 hints:

line 482 col 27: 'body1' is deprecated and shouldn't be used. This is the term used in the 2014 version of material design. The modern term is bodyText2. This feature was deprecated after v1.13.8..

line 505 col 27: 'body1' is deprecated and shouldn't be used. This is the term used in the 2014 version of material design. The modern term is bodyText2. This feature was deprecated after v1.13.8..

line 518 col 27: 'body1' is deprecated and shouldn't be used. This is the term used in the 2014 version of material design. The modern term is bodyText2. This feature was deprecated after v1.13.8..

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.3.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
intl ^0.16.1