timetable 0.2.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 89

馃搮 Customizable, animated calendar widget including day & week views.

Event positioning demoDark mode & custom range
Screenshot of timetableScreenshot of timetable in dark mode with only three visible days

Getting started #

1. Initialize time_machine #

This package uses time_machine for handling date and time, which you first have to initialize.

Add this to your pubspec.yaml:

flutter:
  assets:
    - packages/time_machine/data/cultures/cultures.bin
    - packages/time_machine/data/tzdb/tzdb.bin

Modify your main.dart's main():

import 'package:flutter/services.dart';
import 'package:time_machine/time_machine.dart';

void main() async {
  // Call these two functions before `runApp()`.
  WidgetsFlutterBinding.ensureInitialized();
  await TimeMachine.initialize({'rootBundle': rootBundle});

  runApp(MyApp());
}

Source: https://pub.dev/packages/time_machine#flutter-specific-notes

2. Define your Events #

Events are provided as instances of Event. To get you started, there's the subclass BasicEvent, which you can instantiate directly. If you want to be more specific, you can also implement your own class extending Event.

Note: Most classes of timetable accept a type-parameter E extends Event. Please set it to your chosen Event-subclass (e.g. BasicEvent) to avoid runtime exceptions.

In addition, you also need a Widget to display your events. When using BasicEvent, this can simply be BasicEventWidget.

3. Create an EventProvider #

As the name suggests, you use EventProvider to provide Events to timetable. There are currently two EventProviders to choose from:

final myEventProvider = EventProvider.list([
  BasicEvent(
    id: 0,
    title: 'My Event',
    color: Colors.blue,
    start: LocalDate.today().at(LocalTime(13, 0, 0)),
    end: LocalDate.today().at(LocalTime(15, 0, 0)),
  ),
]);

For trying out the behavior of changing events, you can create a StreamController<List<E>> and add() different lists of events, e.g. in Future.delayed():

final eventController = StreamController<List<BasicEvent>>()..add([]);
final provider = EventProvider.simpleStream(eventController.stream);
Future.delayed(Duration(seconds: 5), () => eventController.add(/* some events */));

// Don't forget to close the stream controller when you're done, e.g. in `dispose`:
eventController.close();

See the example for more EventProvider samples!

4. Create a TimetableController #

Similar to a ScrollController or a TabController, a TimetableController is responsible for interacting with a Timetable and managing its state. You can instantiate it with your EventProvider:

final myController = TimetableController(
  eventProvider: myEventProvider,
  // Optional parameters with their default values:
  initialTimeRange: InitialTimeRange.range(
    startTime: LocalTime(8, 0, 0),
    endTime: LocalTime(20, 0, 0),
  ),
  initialDate: LocalDate.today(),
  visibleRange: VisibleRange.week(),
  firstDayOfWeek: DayOfWeek.monday,
);

Don't forget to dispose your controller, e.g. in State.dispose!

5. Create your Timetable #

Using your TimetableController, you can now create a Timetable widget:

Timetable<BasicEvent>(
  controller: myController,
  eventBuilder: (event) => BasicEventWidget(event),
  allDayEventBuilder: (context, event, info) =>
      BasicAllDayEventWidget(event, info: info),
)

And you're done 馃帀

Theming #

For a full list of visual properties that can be tweaked, see TimetableThemeData.

To apply a theme, specify it in the Timetable constructor:

Timetable<BasicEvent>(
  controller: /* ... */,
  theme: TimetableThemeData(
    primaryColor: Colors.teal,
    partDayEventMinimumDuration: Period(minutes: 30),
    // ...and many more!
  ),
),

Localization #

time_machine is used internally for date & time formatting. By default, it uses en_US as its locale (managed by the Culture class) and doesn't know about Flutter's locale. To change the locale, set Culture.current after the call to TimeMachine.initialize:

// Supported cultures: https://github.com/Dana-Ferguson/time_machine/tree/master/lib/data/cultures
Culture.current = await Cultures.getCulture('de');

To automatically react to locale changes of the app, see Dana-Ferguson/time_machine#28.

Note: A better solution for Localization is already planned.

Features & Coming soon #

  • [x] Smartly arrange overlapping events
  • [x] Zooming
  • [x] Selectable VisibleRanges
  • [x] Display all-day events at the top
  • [x] Theming
  • [ ] Animate between different VisibleRanges: see #17
  • [ ] Month-view, Agenda-view: see #17
  • [x] Listener when tapping the background (e.g. for creating an event)
  • [ ] Support for event resizing

Changelog #

All notable changes to this project will be documented in this file.

This project adheres to Semantic Versioning.

Unreleased #

0.2.5 路 2020-07-06 #

馃摐 Documentation updates #

  • add Localization section to the README

馃摝 Build & CI #

  • update dartx to v0.4.0

0.2.4 路 2020-06-25 #

馃帀 New Features #

  • Timetable.onEventBackgroundTap: called when tapping the background, e.g. for creating an event (#20), closes: #18. Thanks to @raLaaaa!
  • add EventProvider.simpleStream as a simpler interface than EventProvider.stream (e63bfb4)

馃摐 Documentation updates #

  • improve streaming EventProvider documentation (e63bfb4), fixes: #19

0.2.3 路 2020-06-15 #

馃帀 New Features #

  • Customizable date/weekday format with TimetableThemeData.weekDayIndicatorPattern, .dateIndicatorPattern & temporary .totalDateIndicatorHeight (#16), closes: #15

0.2.2 路 2020-05-30 #

馃帀 New Features #

  • optional onTap-parameter for BasicEventWidget & BasicAllDayEventWidget (#12), closes: #11

馃摝 Build & CI #

  • specify minimum Dart version (v2.7.0) in pubspec.yaml

0.2.1 路 2020-05-19 #

馃帀 New Features #

  • All-day events (shown at the top) (#8), closes: #5
  • Theming (#9)鈥夆斺塻ee the README for more information!

馃摝 Build & CI #

  • specify minimum Flutter version (v1.17.0) in pubspec.yaml
  • example: upload generated APK as artifact

0.2.0 路 2020-05-08 #

鈿 BREAKING CHANGES #

  • fix week scroll alignment (#6)
    • To provide a simpler API the exposed methods of VisibleRange were changed slightly. This doesn't affect you if you just instantiate one of the given implementations, but only if you extend it yourself or call one of its methods directly.

馃悰 Bug Fixes #

  • support Flutter v1.17.0 (#4)

0.1.3 路 2020-05-06 #

馃悰 Bug Fixes #

  • fix time zooming & add testing (#3)

0.1.2 路 2020-05-05 #

馃帀 New Features #

  • add TimetableController.initialTimeRange, closes: #1

馃悰 Bug Fixes #

  • fix week alignment with WeekVisibleRange, closes: #2

0.1.1 路 2020-04-02 #

馃摐 Documentation updates #

  • fix broken links in README

0.1.0 路 2020-04-02 #

Initial release 馃帀

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:time_machine/time_machine.dart';
import 'package:timetable/timetable.dart';

// ignore: unused_import
import 'positioning_demo.dart';
import 'utils.dart';

void main() async {
  setTargetPlatformForDesktop();

  WidgetsFlutterBinding.ensureInitialized();
  await TimeMachine.initialize({'rootBundle': rootBundle});
  runApp(ExampleApp(child: TimetableExample()));
}

class TimetableExample extends StatefulWidget {
  @override
  _TimetableExampleState createState() => _TimetableExampleState();
}

class _TimetableExampleState extends State<TimetableExample> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
  TimetableController<BasicEvent> _controller;

  @override
  void initState() {
    super.initState();

    _controller = TimetableController(
      // A basic EventProvider containing a single event:
      // eventProvider: EventProvider.list([
      //   BasicEvent(
      //     id: 0,
      //     title: 'My Event',
      //     color: Colors.blue,
      //     start: LocalDate.today().at(LocalTime(13, 0, 0)),
      //     end: LocalDate.today().at(LocalTime(15, 0, 0)),
      //   ),
      // ]),

      // For a demo of overlapping events, use this one instead:
      eventProvider: positioningDemoEventProvider,

      // Or even this short example using a Stream:
      // eventProvider: EventProvider.stream(
      //   eventGetter: (range) => Stream.periodic(
      //     Duration(milliseconds: 16),
      //     (i) {
      //       final start =
      //           LocalDate.today().atMidnight() + Period(minutes: i * 2);
      //       return [
      //         BasicEvent(
      //           id: 0,
      //           title: 'Event',
      //           color: Colors.blue,
      //           start: start,
      //           end: start + Period(hours: 5),
      //         ),
      //       ];
      //     },
      //   ),
      // ),

      // Other (optional) parameters:
      initialTimeRange: InitialTimeRange.range(
        startTime: LocalTime(8, 0, 0),
        endTime: LocalTime(20, 0, 0),
      ),
      initialDate: LocalDate.today(),
      visibleRange: VisibleRange.days(3),
      firstDayOfWeek: DayOfWeek.monday,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text('Timetable example'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.today),
            onPressed: () => _controller.animateToToday(),
            tooltip: 'Jump to today',
          ),
        ],
      ),
      body: Timetable<BasicEvent>(
        controller: _controller,
        onEventBackgroundTap: (start, isAllDay) {
          _showSnackBar('Background tapped $start is all day event $isAllDay');
        },
        eventBuilder: (event) {
          return BasicEventWidget(
            event,
            onTap: () => _showSnackBar('Part-day event $event tapped'),
          );
        },
        allDayEventBuilder: (context, event, info) => BasicAllDayEventWidget(
          event,
          info: info,
          onTap: () => _showSnackBar('All-day event $event tapped'),
        ),
      ),
    );
  }

  void _showSnackBar(String content) {
    _scaffoldKey.currentState.showSnackBar(SnackBar(
      content: Text(content),
    ));
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  timetable: ^0.2.5

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:timetable/timetable.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
78
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
89
Learn more about scoring.

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

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • timetable that is a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
black_hole_flutter ^0.2.11 0.2.12
collection ^1.14.11 1.14.12 1.14.13
dartx ^0.4.0 0.4.2
flutter 0.0.0
meta ^1.1.8 1.1.8 1.2.2
pedantic ^1.8.0+1 1.9.0 1.9.2
rxdart ^0.24.0 0.24.1
time_machine ^0.9.12 0.9.12
Transitive dependencies
characters 1.0.0 1.1.0-nullsafety
charcode 1.1.3
convert 2.1.1
crypto 2.1.5
path 1.7.0
resource 2.1.7
sky_engine 0.0.99
time 1.3.0
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test
test ^1.9.4