Initial commit
This commit is contained in:
27
lib/application.dart
Normal file
27
lib/application.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reverse_nn/pages/home_page.dart';
|
||||
import 'package:reverse_nn/ui/screens/home_screen.dart';
|
||||
|
||||
class ReversNNApplication extends StatelessWidget {
|
||||
const ReversNNApplication({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetMaterialApp(
|
||||
title: 'Реверс НН',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal, brightness: Brightness.light),
|
||||
useMaterial3: true,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal, brightness: Brightness.dark),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const HomeScreen(),
|
||||
|
||||
defaultTransition: Transition.noTransition,
|
||||
transitionDuration: const Duration(seconds: 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
19
lib/application/controllers/application_controller.dart
Normal file
19
lib/application/controllers/application_controller.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ApplicationController extends GetxController {
|
||||
IconData getThemeIcon() => Get.isDarkMode
|
||||
? Icons.light_mode
|
||||
: Icons.dark_mode;
|
||||
|
||||
void toggleTheme() {
|
||||
Get.changeThemeMode(
|
||||
Get.isDarkMode
|
||||
? ThemeMode.light
|
||||
: ThemeMode.dark
|
||||
);
|
||||
update();
|
||||
}
|
||||
}
|
||||
5
lib/application/controllers/home_controller.dart
Normal file
5
lib/application/controllers/home_controller.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class HomeController extends GetxController {
|
||||
|
||||
}
|
||||
14
lib/application/controllers/schedule_controller.dart
Normal file
14
lib/application/controllers/schedule_controller.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reverse_nn/application/services/schedule.dart';
|
||||
|
||||
class ScheduleController extends GetxController {
|
||||
Rx<Map<String, dynamic>?> currentSchedule = null.obs;
|
||||
|
||||
@override void onReady() {
|
||||
super.onReady();
|
||||
ScheduleService().getCurrentStatus().then((currentSchedule) {
|
||||
this.currentSchedule = currentSchedule.obs;
|
||||
update();
|
||||
});
|
||||
}
|
||||
}
|
||||
132
lib/application/services/schedule.dart
Normal file
132
lib/application/services/schedule.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'dart:core';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:intl/intl.dart' show DateFormat;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ScheduleService {
|
||||
static const Duration dayOffset = Duration(hours: 4);
|
||||
|
||||
Future<Map<String, dynamic>> getSchema() async {
|
||||
final source = await rootBundle
|
||||
.loadString('assets/schedule.json');
|
||||
return jsonDecode(source);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getRule(DateTime day) async {
|
||||
final Map<String, dynamic> schema = await getSchema();
|
||||
if(!schema.containsKey('rules')) return null;
|
||||
|
||||
for(final Map<String, dynamic> rule in (schema['rules'] as List<dynamic>)) {
|
||||
final DateTime after = DateTime.parse(rule['after']);
|
||||
final DateTime? before = rule['before'] != null
|
||||
? DateTime.parse(rule['before'])
|
||||
: null;
|
||||
|
||||
if(day.isAfter(after) && (before == null || day.isBefore(before)))
|
||||
{ return rule; }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, String>?> getDataset(DateTime day) async {
|
||||
final Map<String, dynamic>? rule = await getRule(day);
|
||||
|
||||
if(rule == null || !rule.containsKey('dataset')) return null;
|
||||
|
||||
return (rule['dataset'] as Map<String, dynamic>).map((key, value) {
|
||||
return MapEntry(key, value.toString());
|
||||
});
|
||||
}
|
||||
|
||||
String datetimeWeekdayToDatasetKey(int weekday) {
|
||||
switch(weekday){
|
||||
case DateTime.monday: return 'monday';
|
||||
case DateTime.tuesday: return 'tuesday';
|
||||
case DateTime.wednesday:return 'wednesday';
|
||||
case DateTime.thursday: return 'thursday';
|
||||
case DateTime.friday: return 'friday';
|
||||
case DateTime.saturday: return 'saturday';
|
||||
case DateTime.sunday: return 'sunday';
|
||||
}
|
||||
|
||||
return 'monday';
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>?> getScheduleByKey(String key) async {
|
||||
final Map<String, dynamic> schema = await getSchema();
|
||||
if(!schema.containsKey('schedules')) return null;
|
||||
if(!(schema['schedules'] as Map<String, dynamic>).containsKey(key)) return null;
|
||||
|
||||
return (schema['schedules'][key] as List<dynamic>).map((element) {
|
||||
return element as Map<String, dynamic>;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>?> getScheduleByDate(DateTime datetime) async {
|
||||
final datetimeStartWithOffset = datetime.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0)
|
||||
.add(dayOffset);
|
||||
|
||||
final DateTime usedScheduleDatetime = datetime.isAfter(datetimeStartWithOffset)
|
||||
? datetime
|
||||
: datetime.copyWith().subtract(const Duration(days: 1));
|
||||
|
||||
final Map<String, String>? dataset = await getDataset(usedScheduleDatetime);
|
||||
if(dataset == null) return null;
|
||||
|
||||
final String dayKey = DateFormat('y-M-d').format(usedScheduleDatetime);
|
||||
final String weekdayKey = datetimeWeekdayToDatasetKey(usedScheduleDatetime.weekday);
|
||||
|
||||
String? scheduleKey;
|
||||
if(dataset.containsKey(dayKey)) { scheduleKey = dataset[dayKey]; }
|
||||
else if(dataset.containsKey(weekdayKey)) { scheduleKey = dataset[weekdayKey]; }
|
||||
|
||||
if(scheduleKey == null) return null;
|
||||
|
||||
return await getScheduleByKey(scheduleKey);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getCurrentStatus() async {
|
||||
final DateTime now = DateTime.now();
|
||||
final List<Map<String, dynamic>>? schedule = await getScheduleByDate(now);
|
||||
if(schedule == null) return null;
|
||||
|
||||
DateTime datetimeStartWithOffset = now.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0)
|
||||
.add(dayOffset);
|
||||
|
||||
if(now.isBefore(datetimeStartWithOffset)) {
|
||||
datetimeStartWithOffset = datetimeStartWithOffset.subtract(const Duration(days: 1));
|
||||
}
|
||||
|
||||
int durationOffset = 0;
|
||||
for (final Map<String, dynamic> scheduleItem in schedule) {
|
||||
final int duration = (scheduleItem['duration'] as int);
|
||||
final DateTime start = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset));
|
||||
final DateTime end = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset + duration));
|
||||
durationOffset += duration;
|
||||
|
||||
if(now.isAfter(start) && now.isBefore(end)) {
|
||||
return <String, dynamic>{
|
||||
"direction": scheduleItem['direction'] as String,
|
||||
"start": start,
|
||||
"end": end
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
|
||||
|
||||
// final datetimeStartWithOffset = now.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0)
|
||||
// .add(dayOffset);
|
||||
//
|
||||
// final DateTime usedScheduleDatetime = now.isAfter(datetimeStartWithOffset)
|
||||
// ? now
|
||||
// : now.copyWith().subtract(const Duration(days: 1));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
11
lib/enums/direction.dart
Normal file
11
lib/enums/direction.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
enum Direction {
|
||||
inCity(displayName: 'В город'),
|
||||
change(displayName: 'Переключение'),
|
||||
outCity(displayName: 'Из города');
|
||||
|
||||
const Direction({
|
||||
required this.displayName
|
||||
});
|
||||
|
||||
final String displayName;
|
||||
}
|
||||
67
lib/enums/schedule.dart
Normal file
67
lib/enums/schedule.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:reverse_nn/enums/direction.dart';
|
||||
import 'package:sprintf/sprintf.dart';
|
||||
|
||||
enum Schedule {
|
||||
daily(elements: [
|
||||
ScheduleElement(startHours: 4, startMinutes: 0, duration: 415, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 10, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 11, startMinutes: 0, duration: 115, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 12, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 13, startMinutes: 0, duration: 145, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 15, startMinutes: 25, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 15, startMinutes: 30, duration: 205, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 18, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 19, startMinutes: 0, duration: 55, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 19, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 20, startMinutes: 0, duration: 475, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 3, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
]),
|
||||
friday(elements: [
|
||||
ScheduleElement(startHours: 4, startMinutes: 0, duration: 415, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 10, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 11, startMinutes: 0, duration: 115, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 12, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 13, startMinutes: 0, duration: 55, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 13, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 14, startMinutes: 0, duration: 840, direction: Direction.outCity),
|
||||
]),
|
||||
saturday(elements: [
|
||||
ScheduleElement(startHours: 4, startMinutes: 0, duration: 655, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 14, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 15, startMinutes: 0, duration: 175, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 17, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 18, startMinutes: 0, duration: 115, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 19, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 20, startMinutes: 0, duration: 115, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 21, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 22, startMinutes: 0, duration: 355, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 3, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
]),
|
||||
sunday(elements: [
|
||||
ScheduleElement(startHours: 4, startMinutes: 0, duration: 235, direction: Direction.inCity),
|
||||
ScheduleElement(startHours: 7, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 8, startMinutes: 0, duration: 175, direction: Direction.outCity),
|
||||
ScheduleElement(startHours: 10, startMinutes: 55, duration: 5, direction: Direction.change),
|
||||
ScheduleElement(startHours: 11, startMinutes: 0, duration: 1020, direction: Direction.inCity),
|
||||
]);
|
||||
|
||||
const Schedule({required this.elements});
|
||||
|
||||
final List<ScheduleElement> elements;
|
||||
}
|
||||
|
||||
class ScheduleElement {
|
||||
final int startHours;
|
||||
final int startMinutes;
|
||||
|
||||
final int duration;
|
||||
final Direction direction;
|
||||
|
||||
const ScheduleElement({required this.startHours, required this.startMinutes, required this.duration, required this.direction});
|
||||
|
||||
String getStartedTimeString() { return sprintf('%02i:%02i', [startHours, startMinutes]); }
|
||||
String getEndedTimeString() {
|
||||
DateTime e = DateTime(2024, 8, 1, startHours, startMinutes).add(Duration(minutes: duration)).subtract(const Duration(microseconds: 1));
|
||||
return sprintf('%02i:%02i', [e.hour, e.minute]);
|
||||
}
|
||||
}
|
||||
6
lib/main.dart
Normal file
6
lib/main.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reverse_nn/application.dart';
|
||||
|
||||
void main() => runApp(const ReversNNApplication());
|
||||
|
||||
|
||||
18
lib/pages/home_page.dart
Normal file
18
lib/pages/home_page.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reverse_nn/widgets/schedule_widget.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
title: const Text('Реверс НН'),
|
||||
),
|
||||
body: const ScheduleWidget()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
47
lib/ui/components/current_status_component.dart
Normal file
47
lib/ui/components/current_status_component.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart' show DateFormat;
|
||||
import 'package:reverse_nn/application/controllers/schedule_controller.dart';
|
||||
|
||||
class CurrentStatusComponent extends StatelessWidget {
|
||||
const CurrentStatusComponent({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ScheduleController controller = Get.put(ScheduleController());
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: GetBuilder<ScheduleController>(
|
||||
builder: (scheduleController) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.exit_to_app, size: 96),
|
||||
const SizedBox(width: 16),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if(scheduleController.currentSchedule.value != null) ...[
|
||||
Text(scheduleController.currentSchedule.value?['direction'] ?? '', style: Theme.of(context).textTheme.headlineLarge),
|
||||
Text('C ${formatDate(scheduleController.currentSchedule.value?['start'])} До ${formatDate(scheduleController.currentSchedule.value!['end'])}', style: Theme.of(context).textTheme.titleMedium)
|
||||
]
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String formatDate(DateTime date) => DateFormat('hh:mm').format(date);
|
||||
|
||||
}
|
||||
33
lib/ui/components/grid_menu_item.dart
Normal file
33
lib/ui/components/grid_menu_item.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GridMenuItem extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final void Function()? onTap;
|
||||
|
||||
const GridMenuItem({super.key, required this.icon, required this.label, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, size: 64),
|
||||
const SizedBox(height: 6),
|
||||
Text(label, style: Theme.of(context).textTheme.titleMedium, textAlign: TextAlign.center)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
26
lib/ui/layouts/application_layout.dart
Normal file
26
lib/ui/layouts/application_layout.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reverse_nn/application/controllers/application_controller.dart';
|
||||
|
||||
class ApplicationLayout extends StatelessWidget {
|
||||
final Widget body;
|
||||
const ApplicationLayout({super.key, required this.body});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ApplicationController applicationController = Get.put(ApplicationController());
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
title: const Text('Реверс НН'),
|
||||
actions: [
|
||||
IconButton(icon: Icon(
|
||||
applicationController.getThemeIcon()),
|
||||
onPressed: applicationController.toggleTheme
|
||||
),
|
||||
],
|
||||
),
|
||||
body: body,
|
||||
);
|
||||
}
|
||||
}
|
||||
48
lib/ui/screens/home_screen.dart
Normal file
48
lib/ui/screens/home_screen.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reverse_nn/application/controllers/home_controller.dart';
|
||||
import 'package:reverse_nn/application/services/schedule.dart';
|
||||
import 'package:reverse_nn/ui/components/current_status_component.dart';
|
||||
import 'package:reverse_nn/ui/components/grid_menu_item.dart';
|
||||
import 'package:reverse_nn/ui/layouts/application_layout.dart';
|
||||
|
||||
class HomeScreen extends GetWidget<HomeController> {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ApplicationLayout(body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const CurrentStatusComponent(),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: GridView(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
),
|
||||
children: [
|
||||
GridMenuItem(icon: Icons.calendar_month, label: 'Расписание', onTap: () {
|
||||
// log(DateTime(2024, 8, 1, 0, 0, 0).toIso8601String());
|
||||
ScheduleService().getCurrentStatus().then((val) {
|
||||
log(val.toString());
|
||||
});
|
||||
}),
|
||||
const GridMenuItem(icon: Icons.monetization_on, label: 'Поддержать автора'),
|
||||
// GridMenuItem(),
|
||||
// GridMenuItem(),
|
||||
]
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
36
lib/utils/scheduler.dart
Normal file
36
lib/utils/scheduler.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:reverse_nn/enums/schedule.dart';
|
||||
|
||||
class Scheduler {
|
||||
static Schedule getSchedule(DateTime date) {
|
||||
switch(date.weekday) {
|
||||
case DateTime.friday: return Schedule.friday;
|
||||
case DateTime.saturday: return Schedule.saturday;
|
||||
case DateTime.sunday: return Schedule.sunday;
|
||||
|
||||
default: return Schedule.daily;
|
||||
}
|
||||
}
|
||||
|
||||
static List<ScheduleElement> getScheduleList() {
|
||||
List<ScheduleElement> result = List<ScheduleElement>.empty(growable: true);
|
||||
|
||||
DateTime now = DateTime.now();
|
||||
DateTime startSchedule = now.hour < 4 ? now.subtract(const Duration(days: 1)) : now;
|
||||
|
||||
bool currentReached = false;
|
||||
for (ScheduleElement element in [...Scheduler.getSchedule(startSchedule).elements, ...Scheduler.getSchedule(startSchedule.add(const Duration(days: 1))).elements]) {
|
||||
if(!currentReached) {
|
||||
DateTime s = DateTime(now.year, now.month, now.day, element.startHours, element.startMinutes, 0);
|
||||
if(now.compareTo(startSchedule) != 0 && element.startHours >= 4) { s = s.subtract(const Duration(days: 1)); }
|
||||
DateTime e = s.add(Duration(minutes: element.duration)).subtract(const Duration(microseconds: 1));
|
||||
|
||||
if(now.isAfter(s) && now.isBefore(e)) { currentReached = true; }
|
||||
else { continue; }
|
||||
}
|
||||
|
||||
result.add(element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
16
lib/widgets/loader_widget.dart
Normal file
16
lib/widgets/loader_widget.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoaderWidget extends StatelessWidget {
|
||||
const LoaderWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator()
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
94
lib/widgets/schedule_element_widget.dart
Normal file
94
lib/widgets/schedule_element_widget.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reverse_nn/enums/direction.dart';
|
||||
import 'package:reverse_nn/enums/schedule.dart';
|
||||
import 'package:sprintf/sprintf.dart';
|
||||
|
||||
class CurrentReversWidget extends StatelessWidget {
|
||||
const CurrentReversWidget({super.key, required this.scheduleElement});
|
||||
|
||||
final ScheduleElement scheduleElement;
|
||||
|
||||
IconData _getIconData() {
|
||||
switch(scheduleElement.direction) {
|
||||
case Direction.inCity: return Icons.arrow_upward;
|
||||
case Direction.outCity: return Icons.arrow_downward;
|
||||
case Direction.change: return Icons.cancel_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
||||
color: Theme.of(context).colorScheme.primaryContainer
|
||||
),
|
||||
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
Icon(_getIconData(), size: 56.0),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Реверс: ${scheduleElement.direction.displayName}', style: const TextStyle(fontSize: 20)),
|
||||
Text('${scheduleElement.getStartedTimeString()} - ${scheduleElement.getEndedTimeString()}', style: const TextStyle(fontSize: 36)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// children: [
|
||||
// const Text('Текущий статус реверса:', style: TextStyle(
|
||||
// color: Colors.black87,
|
||||
// fontSize: 14.0
|
||||
// )),
|
||||
// Text(scheduleElement.direction.displayName, style: const TextStyle(
|
||||
// color: Colors.black,
|
||||
// fontSize: 32.0
|
||||
// ),),
|
||||
// ],
|
||||
// ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduledReversWidget extends StatelessWidget {
|
||||
const ScheduledReversWidget({super.key, required this.scheduleElement});
|
||||
|
||||
final ScheduleElement scheduleElement;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
||||
color: Theme.of(context).colorScheme.secondaryContainer
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(sprintf('Следующий статус в %02i:%02i - %s', [
|
||||
scheduleElement.startHours,
|
||||
scheduleElement.startMinutes,
|
||||
scheduleElement.direction.displayName
|
||||
])),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
45
lib/widgets/schedule_widget.dart
Normal file
45
lib/widgets/schedule_widget.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reverse_nn/enums/schedule.dart';
|
||||
import 'package:reverse_nn/utils/scheduler.dart';
|
||||
import 'package:reverse_nn/widgets/loader_widget.dart';
|
||||
import 'package:reverse_nn/widgets/schedule_element_widget.dart';
|
||||
|
||||
class ScheduleWidget extends StatefulWidget {
|
||||
const ScheduleWidget({super.key});
|
||||
|
||||
@override
|
||||
State<ScheduleWidget> createState() => _ScheduleWidgetState();
|
||||
}
|
||||
|
||||
class _ScheduleWidgetState extends State<ScheduleWidget> {
|
||||
bool _loading = true;
|
||||
List<ScheduleElement> _schedule = List.empty();
|
||||
|
||||
void loadScheduleData() async {
|
||||
setState(() { _loading = true; });
|
||||
setState(() { _schedule = Scheduler.getScheduleList(); });
|
||||
setState(() { _loading = false; });
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
loadScheduleData();
|
||||
|
||||
Future.delayed(const Duration(seconds: 15), () => loadScheduleData());
|
||||
|
||||
return _loading && _schedule.isNotEmpty ? const LoaderWidget() : ListView.separated(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10),
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return SizedBox(height: index == 0 ? 10 : 4);
|
||||
},
|
||||
itemCount: _schedule.length,
|
||||
itemBuilder: (ctx, i) {
|
||||
if(i == 0) { return CurrentReversWidget(scheduleElement: _schedule[i]); }
|
||||
return ScheduledReversWidget(scheduleElement: _schedule[i]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user