@@ -12,15 +12,21 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
1212 Duration timerUpdateInterval; // Milliseconds
1313 double initialZoomLevel; // Seconds per screen width
1414 DateTime ? initialTime;
15+ DateTime ? minCursor;
16+ DateTime ? maxCursor;
1517 InteractiveTimelineCubit ({
1618 this .minSecondsPerScreenWidth = 10 ,
1719 this .maxSecondsPerScreenWidth = 10000 ,
1820 this .timerUpdateInterval = const Duration (milliseconds: 25 ),
1921 this .initialZoomLevel = 500 ,
2022 this .initialTime,
23+ minCursor,
24+ maxCursor,
2125 }) : super (
2226 InteractiveTimelineState .initializeAtTime (
2327 initialTime ?? DateTime .now (),
28+ minCursor,
29+ maxCursor,
2430 ),
2531 );
2632
@@ -40,12 +46,13 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
4046
4147 void dragHorizontally (DragUpdateDetails update) {
4248 num offsetSeconds = update.primaryDelta! * state.secondsPerPixel;
43- emit (state.overwrite (
44- middleCursor: state.middleCursor.subtract (
45- Duration (milliseconds: (offsetSeconds * 1000 ).toInt ()),
46- ),
47- isInteracting: true ,
48- ));
49+ DateTime newCursor = state.middleCursor.subtract (Duration (milliseconds: (offsetSeconds * 1000 ).toInt ()));
50+ state.insideMinMaxCursor (newCursor)
51+ ? emit (state.overwrite (
52+ middleCursor: newCursor,
53+ isInteracting: true ,
54+ ))
55+ : {};
4956 }
5057
5158 void zoomStart (ScaleStartDetails details) {
@@ -97,14 +104,22 @@ class InteractiveTimelineCubit extends Cubit<InteractiveTimelineState> {
97104 }
98105 }
99106
107+ // TODO: Using null won't clear the variables
108+ void setMinMax ({DateTime ? min, DateTime ? max}) => emit (state.overwrite (minCursor: min, maxCursor: max));
109+
100110 void _tickTimer (Duration duration) {
101111 if (! state.isInteracting) {
102112 double timerMilliesecondsPerScreenWidth = 10 * 1000 ; // timer scroll speed
103113 double deltaScreenWidth = duration.inMilliseconds / timerMilliesecondsPerScreenWidth;
104114 Duration deltaCursor = Duration (milliseconds: (deltaScreenWidth * state.secondsPerScreenWidth * 1000 ).toInt ());
105- emit (state.overwrite (
106- middleCursor: state.middleCursor.add (deltaCursor),
107- ));
115+ DateTime newCursor = state.middleCursor.add (deltaCursor);
116+ state.insideMinMaxCursor (newCursor)
117+ ? emit (
118+ state.overwrite (
119+ middleCursor: newCursor,
120+ ),
121+ )
122+ : stopTimer ();
108123 }
109124 }
110125
@@ -128,6 +143,8 @@ class InteractiveTimelineState extends Equatable {
128143 required this .middleCursor,
129144 required this .leftCursor,
130145 required this .rightCursor,
146+ required this .minCursor,
147+ required this .maxCursor,
131148 required this .playTimer,
132149 required this .isPlaying,
133150 required this .isInteracting,
@@ -141,14 +158,16 @@ class InteractiveTimelineState extends Equatable {
141158 final DateTime middleCursor;
142159 final DateTime leftCursor;
143160 final DateTime rightCursor;
161+ final DateTime ? minCursor;
162+ final DateTime ? maxCursor;
144163 final Timer ? playTimer;
145164 final bool isPlaying;
146165 final bool isInteracting;
147166
148167 @override
149168 List <Object > get props => [width, height, secondsPerPixel, secondsPerScreenWidth, secondsPerScreenWidthBeforeZoom, middleCursor, leftCursor, rightCursor, isPlaying, isInteracting];
150169
151- static InteractiveTimelineState initializeAtTime (DateTime time) {
170+ static InteractiveTimelineState initializeAtTime (DateTime time, DateTime ? minCursor, DateTime ? maxCursor ) {
152171 return InteractiveTimelineState (
153172 width: 0 ,
154173 height: 0 ,
@@ -158,6 +177,8 @@ class InteractiveTimelineState extends Equatable {
158177 middleCursor: time,
159178 leftCursor: time,
160179 rightCursor: time,
180+ minCursor: minCursor,
181+ maxCursor: maxCursor,
161182 playTimer: null ,
162183 isPlaying: false ,
163184 isInteracting: false ,
@@ -176,12 +197,20 @@ class InteractiveTimelineState extends Equatable {
176197 );
177198 }
178199
200+ bool insideMinMaxCursor (DateTime time) {
201+ if (minCursor != null ) if (time.isBefore (minCursor! )) return false ;
202+ if (maxCursor != null ) if (time.isAfter (maxCursor! )) return false ;
203+ return true ;
204+ }
205+
179206 InteractiveTimelineState overwrite ({
180207 double ? width,
181208 double ? height,
182209 double ? secondsPerScreenWidth,
183210 double ? secondsPerScreenWidthBeforeZoom,
184211 DateTime ? middleCursor,
212+ DateTime ? minCursor,
213+ DateTime ? maxCursor,
185214 Timer ? playTimer,
186215 bool ? isPlaying,
187216 bool ? isInteracting,
@@ -198,6 +227,8 @@ class InteractiveTimelineState extends Equatable {
198227 middleCursor: middleCursor ?? this .middleCursor,
199228 leftCursor: getLeftCursor (width ?? this .width, newSecondsPerPixel),
200229 rightCursor: getRightCursor (width ?? this .width, newSecondsPerPixel),
230+ minCursor: minCursor ?? this .minCursor,
231+ maxCursor: maxCursor ?? this .maxCursor,
201232 playTimer: playTimer ?? this .playTimer,
202233 isPlaying: isPlaying ?? this .isPlaying,
203234 isInteracting: isInteracting ?? this .isInteracting,
@@ -206,9 +237,9 @@ class InteractiveTimelineState extends Equatable {
206237}
207238
208239class InteractiveTimelineBlocBuilder extends StatelessWidget {
209- InteractiveTimelineCubit bloc;
210- Widget Function (BuildContext , InteractiveTimelineState ) builder;
211- InteractiveTimelineBlocBuilder ({
240+ final InteractiveTimelineCubit bloc;
241+ final Widget Function (BuildContext , InteractiveTimelineState ) builder;
242+ const InteractiveTimelineBlocBuilder ({
212243 Key ? key,
213244 required this .bloc,
214245 required this .builder,
0 commit comments