Skip to content

Commit

Permalink
setCursor
Browse files Browse the repository at this point in the history
  • Loading branch information
1cedsoda committed Sep 19, 2022
1 parent e160067 commit 68a8b7f
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 64 deletions.
Binary file added example/flutter_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 18 additions & 6 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:example/state_visualizer.dart';
import 'package:flutter/material.dart';
import 'package:interactive_timeline/interactive_timeline.dart';

Expand Down Expand Up @@ -32,10 +31,23 @@ class MyHomePage extends StatefulWidget {
}

class _MyHomePageState extends State<MyHomePage> {
InteractiveTimelineCubit timelineCubit = InteractiveTimelineCubit(
minCursor: DateTime.now().subtract(Duration(hours: 1)),
maxCursor: DateTime.now().add(Duration(hours: 1)),
);
InteractiveTimelineCubit timelineCubit = InteractiveTimelineCubit();

@override
void initState() {
// TODO: implement initState
super.initState();
// after build
WidgetsBinding.instance.addPostFrameCallback((_) {
timelineCubit.setMinMax(
minCursor: DateTime.now().subtract(Duration(hours: 1)),
maxCursor: DateTime.now().add(Duration(hours: 1)),
);
timelineCubit.setCursor(
DateTime.now().subtract(Duration(minutes: 30)),
);
});
}

@override
Widget build(BuildContext context) {
Expand All @@ -47,7 +59,7 @@ class _MyHomePageState extends State<MyHomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StateVisualizer(cubit: timelineCubit),
InteractiveTimelineDebug(cubit: timelineCubit),
InteractiveTimeline(
width: MediaQuery.of(context).size.width,
height: 100,
Expand Down
36 changes: 0 additions & 36 deletions example/lib/state_visualizer.dart

This file was deleted.

1 change: 1 addition & 0 deletions lib/interactive_timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ library interactive_timeline;
export 'src/cubit.dart';
export 'src/widget.dart';
export 'src/utils.dart';
export 'src/debug.dart';
77 changes: 57 additions & 20 deletions lib/src/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:math';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:interactive_timeline/src/utils.dart' as utils;

// timeline cubit
class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
Expand All @@ -23,11 +24,7 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
minCursor,
maxCursor,
}) : super(
InteractiveTimelineState.initializeAtTime(
initialTime ?? DateTime.now(),
minCursor,
maxCursor,
),
InteractiveTimelineState.initializeAtTime(initialTime ?? DateTime.now()),
);

double _restrictZoomLevel(double secondsPerScreenWidth) {
Expand Down Expand Up @@ -104,8 +101,21 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
}
}

// TODO: Using null won't clear the variables
void setMinMax({DateTime? min, DateTime? max}) => emit(state.overwrite(minCursor: min, maxCursor: max));
void setMinMax({DateTime? minCursor, DateTime? maxCursor}) {
var newState = state.setMinMaxCursor(minCursor: minCursor, maxCursor: maxCursor);
newState = newState.overwrite(
middleCursor: newState.cropMinMaxCursor(newState.middleCursor),
);
emit(newState);
}

void setCursor(DateTime cursor) {
emit(
state.overwrite(
middleCursor: state.cropMinMaxCursor(cursor),
),
);
}

void _tickTimer(Duration duration) {
if (!state.isInteracting) {
Expand All @@ -123,12 +133,14 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
}
}

void initialize(double width, double height) {
void initialize(double width, double height, [DateTime? minCursor, DateTime? maxCursor]) {
emit(state.overwrite(
secondsPerScreenWidth: initialZoomLevel, // default zoom level
middleCursor: initialTime,
width: width,
height: height,
minCursor: minCursor,
maxCursor: maxCursor,
));
}
}
Expand Down Expand Up @@ -167,24 +179,36 @@ class InteractiveTimelineState extends Equatable {
@override
List<Object> get props => [width, height, secondsPerPixel, secondsPerScreenWidth, secondsPerScreenWidthBeforeZoom, middleCursor, leftCursor, rightCursor, isPlaying, isInteracting];

static InteractiveTimelineState initializeAtTime(DateTime time, DateTime? minCursor, DateTime? maxCursor) {
static InteractiveTimelineState initializeAtTime(DateTime time) {
return InteractiveTimelineState(
width: 0,
height: 0,
secondsPerPixel: 0,
secondsPerScreenWidth: 0,
secondsPerScreenWidthBeforeZoom: 0,
width: 1,
height: 1,
secondsPerPixel: 1,
secondsPerScreenWidth: 1,
secondsPerScreenWidthBeforeZoom: 1,
middleCursor: time,
leftCursor: time,
rightCursor: time,
minCursor: minCursor,
maxCursor: maxCursor,
minCursor: null,
maxCursor: null,
playTimer: null,
isPlaying: false,
isInteracting: false,
);
}

bool insideMinMaxCursor(DateTime cursor, [DateTime? minCursor, DateTime? maxCursor]) => utils.insideMinMaxCursor(
cursor,
minCursor ?? this.minCursor,
maxCursor ?? this.maxCursor,
);

DateTime cropMinMaxCursor(DateTime cursor, [DateTime? minCursor, DateTime? maxCursor]) => utils.cropMinMaxCursor(
cursor,
minCursor ?? this.minCursor,
maxCursor ?? this.maxCursor,
);

DateTime getLeftCursor(double width, double secondsPerPixel) {
return middleCursor.subtract(
Duration(milliseconds: ((width / 2) * secondsPerPixel * 1000).toInt()),
Expand All @@ -197,10 +221,23 @@ class InteractiveTimelineState extends Equatable {
);
}

bool insideMinMaxCursor(DateTime time) {
if (minCursor != null) if (time.isBefore(minCursor!)) return false;
if (maxCursor != null) if (time.isAfter(maxCursor!)) return false;
return true;
// minCursor and maxCursor should be changable to null (.overwrite would fall back when passed null)
InteractiveTimelineState setMinMaxCursor({DateTime? minCursor, DateTime? maxCursor}) {
return InteractiveTimelineState(
width: width,
height: height,
secondsPerPixel: secondsPerPixel,
secondsPerScreenWidth: secondsPerScreenWidth,
secondsPerScreenWidthBeforeZoom: secondsPerScreenWidthBeforeZoom,
middleCursor: middleCursor,
leftCursor: leftCursor,
rightCursor: rightCursor,
minCursor: minCursor,
maxCursor: maxCursor,
playTimer: playTimer,
isPlaying: isPlaying,
isInteracting: isInteracting,
);
}

InteractiveTimelineState overwrite({
Expand Down
36 changes: 36 additions & 0 deletions lib/src/debug.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:interactive_timeline/interactive_timeline.dart';

class InteractiveTimelineDebug extends StatelessWidget {
const InteractiveTimelineDebug({
Key? key,
required this.cubit,
}) : super(key: key);
final InteractiveTimelineCubit cubit;

@override
Widget build(BuildContext context) {
return InteractiveTimelineBlocBuilder(
bloc: cubit,
builder: (context, state) {
return Column(
children: [
Text('width: ${state.width}'),
Text('height: ${state.height}'),
Text('secondsPerPixel: ${state.secondsPerPixel}'),
Text('secondsPerScreenWidth: ${state.secondsPerScreenWidth}'),
Text('secondsPerScreenWidthBeforeZoom: ${state.secondsPerScreenWidthBeforeZoom}'),
Text('middleCursor: ${state.middleCursor}'),
Text('leftCursor: ${state.leftCursor}'),
Text('rightCursor: ${state.rightCursor}'),
Text('minCursor: ${state.minCursor}'),
Text('maxCursor: ${state.maxCursor}'),
Text('playTimer: ${state.playTimer}'),
Text('isPlaying: ${state.isPlaying}'),
Text('isInteracting: ${state.isInteracting}'),
],
);
},
);
}
}
12 changes: 12 additions & 0 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,15 @@ class TimelineRenderOptions {
this.candleGroups = defaultCandleGroups,
});
}

bool insideMinMaxCursor(DateTime time, DateTime? minCursor, DateTime? maxCursor) {
if (minCursor != null) if (time.isBefore(minCursor)) return false;
if (maxCursor != null) if (time.isAfter(maxCursor)) return false;
return true;
}

DateTime cropMinMaxCursor(DateTime cursor, DateTime? minCursor, DateTime? maxCursor) {
if (minCursor != null) if (cursor.isBefore(minCursor)) return minCursor;
if (maxCursor != null) if (cursor.isAfter(maxCursor)) return maxCursor;
return cursor;
}
6 changes: 5 additions & 1 deletion lib/src/widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class InteractiveTimeline extends StatefulWidget {
TimelineRenderOptions renderOptions;
EdgeInsets padding;
Color color;
DateTime? minCursor;
DateTime? maxCursor;
InteractiveTimeline({
Key? key,
required this.width,
Expand All @@ -18,6 +20,8 @@ class InteractiveTimeline extends StatefulWidget {
this.renderOptions = const TimelineRenderOptions(),
this.padding = const EdgeInsets.all(0),
this.color = Colors.transparent,
this.minCursor,
this.maxCursor,
}) : super(key: key);

@override
Expand All @@ -27,7 +31,7 @@ class InteractiveTimeline extends StatefulWidget {
class _InteractiveTimelineState extends State<InteractiveTimeline> {
@override
void initState() {
widget.cubit.initialize(widget.width, widget.height);
widget.cubit.initialize(widget.width, widget.height, widget.minCursor, widget.maxCursor);
super.initState();
}

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: interactive_timeline
description: Draggable and zoomable interactive timeline.
version: 1.0.0-dev.3
version: 1.0.0-dev.4
# homepage: https://www.example.com

environment:
Expand Down

0 comments on commit 68a8b7f

Please sign in to comment.