chore: fix Flutter baseline checks
This commit is contained in:
@@ -21,7 +21,7 @@ void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Инициализация workmanager
|
||||
Workmanager().initialize(callbackDispatcher, isInDebugMode: false);
|
||||
Workmanager().initialize(callbackDispatcher);
|
||||
|
||||
runApp(const ProviderScope(child: IgnisApp()));
|
||||
}
|
||||
|
||||
@@ -278,9 +278,10 @@ class GroupsNotifier extends Notifier<List<dynamic>> {
|
||||
|
||||
// Если группа залочена (недавно управляли) -- берём локальное состояние
|
||||
if (_lockUntil.containsKey(id) && _lockUntil[id]!.isAfter(now)) {
|
||||
final existing = state.cast<dynamic?>().firstWhere(
|
||||
(old) => old?['id'].toString() == id,
|
||||
orElse: () => null);
|
||||
final existing = state.firstWhere(
|
||||
(old) => old['id'].toString() == id,
|
||||
orElse: () => null,
|
||||
);
|
||||
return existing ?? map;
|
||||
}
|
||||
|
||||
@@ -311,9 +312,10 @@ class GroupsNotifier extends Notifier<List<dynamic>> {
|
||||
}
|
||||
} catch (e) {
|
||||
// При ошибке опроса -- сохраняем предыдущее состояние
|
||||
final existing = state.cast<dynamic?>().firstWhere(
|
||||
(s) => s?['id'].toString() == id,
|
||||
orElse: () => null);
|
||||
final existing = state.firstWhere(
|
||||
(s) => s['id'].toString() == id,
|
||||
orElse: () => null,
|
||||
);
|
||||
map['last_state'] = existing?['last_state'] ??
|
||||
{'state': false, 'brightness': 100, 'temp': 4000};
|
||||
}
|
||||
|
||||
@@ -47,9 +47,11 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
|
||||
margin: const EdgeInsets.all(12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.deepOrange.withOpacity(0.15),
|
||||
color: Colors.deepOrange.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.deepOrange.withOpacity(0.3)),
|
||||
border: Border.all(
|
||||
color: Colors.deepOrange.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -159,7 +161,7 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
|
||||
SwitchListTile(
|
||||
title: const Text('Администратор'),
|
||||
value: isAdmin,
|
||||
activeColor: Colors.deepOrange,
|
||||
activeThumbColor: Colors.deepOrange,
|
||||
onChanged: (v) => setDialogState(() => isAdmin = v),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
@@ -275,7 +277,7 @@ class _ApiKeyCard extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber.withOpacity(0.2),
|
||||
color: Colors.amber.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: const Text(
|
||||
@@ -289,7 +291,7 @@ class _ApiKeyCard extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.redAccent.withOpacity(0.2),
|
||||
color: Colors.redAccent.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: const Text(
|
||||
@@ -325,7 +327,7 @@ class _ApiKeyCard extends StatelessWidget {
|
||||
String _formatDate(String iso) {
|
||||
try {
|
||||
final d = DateTime.parse(iso);
|
||||
final pad = (int n) => n.toString().padLeft(2, '0');
|
||||
String pad(int n) => n.toString().padLeft(2, '0');
|
||||
return '${pad(d.day)}.${pad(d.month)}.${d.year}';
|
||||
} catch (_) {
|
||||
return iso;
|
||||
|
||||
@@ -156,7 +156,7 @@ class _EventRow extends StatelessWidget {
|
||||
if (iso.isEmpty) return '';
|
||||
try {
|
||||
final d = DateTime.parse(iso);
|
||||
final pad = (int n) => n.toString().padLeft(2, '0');
|
||||
String pad(int n) => n.toString().padLeft(2, '0');
|
||||
return '${pad(d.day)}.${pad(d.month)} ${pad(d.hour)}:${pad(d.minute)}:${pad(d.second)}';
|
||||
} catch (_) {
|
||||
return iso;
|
||||
|
||||
@@ -170,7 +170,7 @@ class _HomeEditScreenState extends ConsumerState<HomeEditScreen> {
|
||||
),
|
||||
),
|
||||
value: _geofenceEnabled,
|
||||
activeColor: Colors.deepOrange,
|
||||
activeThumbColor: Colors.deepOrange,
|
||||
onChanged: _hasCoordinates
|
||||
? (v) => setState(() => _geofenceEnabled = v)
|
||||
: null,
|
||||
|
||||
@@ -15,15 +15,18 @@ class HomesScreen extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _HomesScreenState extends ConsumerState<HomesScreen> {
|
||||
late final UserLocationNotifier _userLocationNotifier;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() => ref.read(userLocationProvider.notifier).startWatching());
|
||||
_userLocationNotifier = ref.read(userLocationProvider.notifier);
|
||||
Future.microtask(() => _userLocationNotifier.startWatching());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
ref.read(userLocationProvider.notifier).stopWatching();
|
||||
_userLocationNotifier.stopWatching();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ class _RemoteScreenState extends ConsumerState<RemoteScreen> {
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.redAccent.withOpacity(0.3),
|
||||
color: Colors.redAccent.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(Icons.delete, color: Colors.redAccent),
|
||||
|
||||
@@ -240,7 +240,7 @@ class _AddScheduleSheetState extends ConsumerState<_AddScheduleSheet> {
|
||||
|
||||
// Выбор группы
|
||||
DropdownButtonFormField<String>(
|
||||
value: _selectedGroupId,
|
||||
initialValue: _selectedGroupId,
|
||||
decoration: const InputDecoration(labelText: 'Группа'),
|
||||
items: groups.map((g) {
|
||||
final id = g['id'].toString();
|
||||
@@ -256,7 +256,7 @@ class _AddScheduleSheetState extends ConsumerState<_AddScheduleSheet> {
|
||||
title: Text(_targetState ? 'Включить' : 'Выключить'),
|
||||
subtitle: const Text('Действие при срабатывании'),
|
||||
value: _targetState,
|
||||
activeColor: Colors.deepOrange,
|
||||
activeThumbColor: Colors.deepOrange,
|
||||
onChanged: (v) => setState(() => _targetState = v),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
|
||||
@@ -73,9 +73,12 @@ Future<bool> executeGeofenceCheck() async {
|
||||
try {
|
||||
// Сначала lastKnown (мгновенно)
|
||||
pos = await Geolocator.getLastKnownPosition();
|
||||
// Если старше 5 минут -- запрашиваем свежую
|
||||
final lastTimestamp = pos?.timestamp;
|
||||
|
||||
// Если позиции нет или она несвежая -- запрашиваем новую
|
||||
if (pos == null ||
|
||||
DateTime.now().difference(pos.timestamp).inMinutes > 5) {
|
||||
lastTimestamp == null ||
|
||||
DateTime.now().difference(lastTimestamp).inMinutes > 5) {
|
||||
pos = await Geolocator.getCurrentPosition(
|
||||
locationSettings: const LocationSettings(
|
||||
accuracy: LocationAccuracy.low,
|
||||
@@ -87,8 +90,6 @@ Future<bool> executeGeofenceCheck() async {
|
||||
return true; // не удалось получить позицию
|
||||
}
|
||||
|
||||
if (pos == null) return true;
|
||||
|
||||
// 4. Считаем расстояние
|
||||
final homeLat = (targetHome['latitude'] as num).toDouble();
|
||||
final homeLon = (targetHome['longitude'] as num).toDouble();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Простой цветовой пикер в виде HSV-слайдеров.
|
||||
|
||||
@@ -23,6 +23,9 @@ class _GroupCardState extends ConsumerState<GroupCard> {
|
||||
double? _localBrightness;
|
||||
double? _localTemp;
|
||||
|
||||
int _channelValue(double channel) =>
|
||||
(channel * 255.0).round().clamp(0, 255);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final g = widget.group;
|
||||
@@ -53,7 +56,10 @@ class _GroupCardState extends ConsumerState<GroupCard> {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: isOn
|
||||
? Border.all(color: cardAccent.withOpacity(0.3), width: 1)
|
||||
? Border.all(
|
||||
color: cardAccent.withValues(alpha: 0.3),
|
||||
width: 1,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Padding(
|
||||
@@ -84,7 +90,7 @@ class _GroupCardState extends ConsumerState<GroupCard> {
|
||||
),
|
||||
Switch(
|
||||
value: isOn,
|
||||
activeColor: Colors.deepOrange,
|
||||
activeThumbColor: Colors.deepOrange,
|
||||
onChanged: (v) =>
|
||||
ref.read(groupsProvider.notifier).toggleGroup(id, v),
|
||||
),
|
||||
@@ -166,7 +172,12 @@ class _GroupCardState extends ConsumerState<GroupCard> {
|
||||
initialColor: Color.fromARGB(255, r, gVal, b),
|
||||
onColorChanged: (c) {
|
||||
// Обновление UI-превью -- через debounce отправляется на сервер
|
||||
ref.read(groupsProvider.notifier).setColor(id, c.red, c.green, c.blue);
|
||||
ref.read(groupsProvider.notifier).setColor(
|
||||
id,
|
||||
_channelValue(c.r),
|
||||
_channelValue(c.g),
|
||||
_channelValue(c.b),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -257,7 +268,9 @@ class _ModeChip extends StatelessWidget {
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? Colors.deepOrange.withOpacity(0.2) : Colors.white10,
|
||||
color: selected
|
||||
? Colors.deepOrange.withValues(alpha: 0.2)
|
||||
: Colors.white10,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: selected
|
||||
? Border.all(color: Colors.deepOrange, width: 1)
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:ignis_app/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const MyApp());
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
testWidgets('app opens homes screen when no homes are configured',
|
||||
(WidgetTester tester) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
await tester.pumpWidget(
|
||||
const ProviderScope(
|
||||
child: IgnisApp(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
expect(find.text('ДОМА'), findsOneWidget);
|
||||
expect(find.text('Нет добавленных домов'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user