idk
This commit is contained in:
parent
c21a43190f
commit
42dfea4048
|
|
@ -0,0 +1,65 @@
|
|||
import '../models/instruction_log.dart';
|
||||
import 'interface/IRepository.dart';
|
||||
|
||||
class InstructionLogRepository implements Repository<InstructionLog> {
|
||||
final List<InstructionLog> _logs = [
|
||||
InstructionLog(
|
||||
id: '1',
|
||||
workerId: '1',
|
||||
instructionId: '1',
|
||||
status: InstructionStatus.completed,
|
||||
notes: 'Работа выполнена качественно'
|
||||
),
|
||||
InstructionLog(
|
||||
id: '2',
|
||||
workerId: '2',
|
||||
instructionId: '2',
|
||||
status: InstructionStatus.assigned,
|
||||
notes: 'В процессе выполнения'
|
||||
),
|
||||
InstructionLog(
|
||||
id: '3',
|
||||
workerId: '1',
|
||||
instructionId: '3',
|
||||
status: InstructionStatus.assigned,
|
||||
notes: 'Ожидает начала работ'
|
||||
)
|
||||
];
|
||||
|
||||
@override
|
||||
Future<List<InstructionLog>> load() async {
|
||||
return List.of(_logs);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<InstructionLog> add(InstructionLog log) async {
|
||||
_logs.add(log);
|
||||
return log;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(InstructionLog log) async {
|
||||
final index = _logs.indexWhere((l) => l.id == log.id);
|
||||
if (index == -1) {
|
||||
throw Exception('Запись журнала не найдена');
|
||||
}
|
||||
_logs[index] = log;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String? id) async {
|
||||
_logs.removeWhere((log) => log.id == id);
|
||||
}
|
||||
|
||||
Future<List<InstructionLog>> getByWorkerId(String workerId) async {
|
||||
return _logs.where((log) => log.workerId == workerId).toList();
|
||||
}
|
||||
|
||||
Future<List<InstructionLog>> getByInstructionId(String instructionId) async {
|
||||
return _logs.where((log) => log.instructionId == instructionId).toList();
|
||||
}
|
||||
|
||||
Future<List<InstructionLog>> getByStatus(InstructionStatus status) async {
|
||||
return _logs.where((log) => log.status == status).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import '../models/worker.dart';
|
||||
import 'interface/IRepository.dart';
|
||||
|
||||
class LocalWorkerRepository implements Repository<Worker> {
|
||||
final List<Worker> _workers = [
|
||||
Worker(
|
||||
id: '1',
|
||||
name: 'Иван Петров',
|
||||
|
||||
),
|
||||
Worker(
|
||||
id: '2',
|
||||
name: 'Мария Сидорова',
|
||||
),
|
||||
Worker(
|
||||
id: '3',
|
||||
name: 'Алексей Козлов',
|
||||
),
|
||||
Worker(
|
||||
id: '4',
|
||||
name: 'Елена Волкова',
|
||||
)
|
||||
];
|
||||
|
||||
@override
|
||||
Future<List<Worker>> load() async {
|
||||
return List.of(_workers);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Worker> add(Worker worker) async {
|
||||
_workers.add(worker);
|
||||
return worker;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(Worker worker) async {
|
||||
var index = _workers.indexWhere((item) => item.id == worker.id);
|
||||
if (index != -1) {
|
||||
_workers[index] = worker;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String? id) async {
|
||||
if (id == null)
|
||||
return;
|
||||
_workers.removeWhere((item) => item.id == id);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,27 +3,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/data/interface/IRepository.dart';
|
||||
import 'package:instruction_app/data/organization_repository.dart';
|
||||
import 'package:instruction_app/data/worker_repository.dart';
|
||||
import 'package:instruction_app/data/instruction_log_repository.dart';
|
||||
import 'package:instruction_app/models/organization.dart';
|
||||
import 'package:instruction_app/models/worker.dart';
|
||||
import 'package:instruction_app/models/instruction_log.dart';
|
||||
import 'package:instruction_app/providers/organization_ptovider.dart';
|
||||
import 'package:instruction_app/screens/auth/login_page.dart';
|
||||
import 'package:instruction_app/screens/organization_page/list_organization_page.dart';
|
||||
import 'package:instruction_app/providers/worker_provider.dart';
|
||||
import 'package:instruction_app/providers/instruction_log_provider.dart';
|
||||
//import 'package:instruction_app/screens/auth/login_page.dart';
|
||||
//import 'package:instruction_app/screens/organization_page/list_organization_page.dart';
|
||||
import 'package:instruction_app/screens/home_page.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'data/instruction_repository.dart';
|
||||
import 'providers/instruction_provider.dart';
|
||||
import 'screens/instruction_list_page.dart';
|
||||
//import 'screens/instruction_list_page.dart';
|
||||
//import 'data/api_instruction_repository.dart';
|
||||
|
||||
void main() {
|
||||
final InstructionRepository repository = LocalInstructionRepository();
|
||||
//LocalInstructionRepository();
|
||||
|
||||
// final Repository<Organization> organizationRepository = LocalOrganizationRepository();
|
||||
final Repository<Organization> organizationRepository = LocalOrganizationRepository();
|
||||
final Repository<Worker> workerRepository = LocalWorkerRepository();
|
||||
final Repository<InstructionLog> instructionLogRepository = InstructionLogRepository();
|
||||
|
||||
runApp(
|
||||
// "Предоставляем" наш провайдер всему дереву виджетов
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => InstructionProvider(repository),
|
||||
// create: (context) => OrganizationProvider(organizationRepository),
|
||||
// "Предоставляем" наши провайдеры всему дереву виджетов
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => InstructionProvider(repository),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => OrganizationProvider(organizationRepository),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => WorkerProvider(workerRepository),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => InstructionLogProvider(instructionLogRepository),
|
||||
),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
|
|
@ -40,7 +61,10 @@ class MyApp extends StatelessWidget {
|
|||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const AuthorizationPage(),
|
||||
// Для разработки используем HomePage напрямую
|
||||
// В продакшене можно вернуть AuthorizationPage
|
||||
home: const HomePage(),
|
||||
// home: const AuthorizationPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
enum InstructionStatus {
|
||||
assigned,
|
||||
completed,
|
||||
overdue,
|
||||
cancelled
|
||||
}
|
||||
|
||||
extension InstructionStatusExtension on InstructionStatus {
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case InstructionStatus.assigned:
|
||||
return 'Назначен';
|
||||
case InstructionStatus.completed:
|
||||
return 'Завершен';
|
||||
case InstructionStatus.overdue:
|
||||
return 'Просрочен';
|
||||
case InstructionStatus.cancelled:
|
||||
return 'Отменен';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InstructionLog {
|
||||
final String? id;
|
||||
final String workerId;
|
||||
final String instructionId;
|
||||
final InstructionStatus status;
|
||||
final String? notes;
|
||||
|
||||
InstructionLog({
|
||||
this.id,
|
||||
required this.workerId,
|
||||
required this.instructionId,
|
||||
required this.status,
|
||||
this.notes,
|
||||
});
|
||||
|
||||
InstructionLog copyWith({
|
||||
String? id,
|
||||
String? workerId,
|
||||
String? instructionId,
|
||||
InstructionStatus? status,
|
||||
String? notes,
|
||||
}) {
|
||||
return InstructionLog(
|
||||
id: id ?? this.id,
|
||||
workerId: workerId ?? this.workerId,
|
||||
instructionId: instructionId ?? this.instructionId,
|
||||
status: status ?? this.status,
|
||||
notes: notes ?? this.notes,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
class Worker {
|
||||
final String? id;
|
||||
final String name;
|
||||
|
||||
Worker({
|
||||
this.id,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
Worker copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
}) {
|
||||
return Worker(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
};
|
||||
}
|
||||
|
||||
factory Worker.fromJson(Map<String, dynamic> json) {
|
||||
return Worker(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import '../models/instruction_log.dart';
|
||||
import '../data/interface/IRepository.dart';
|
||||
|
||||
class InstructionLogProvider extends ChangeNotifier {
|
||||
final Repository<InstructionLog> _repository;
|
||||
List<InstructionLog> _logs = [];
|
||||
bool _isLoading = false;
|
||||
|
||||
InstructionLogProvider(this._repository);
|
||||
|
||||
List<InstructionLog> get logs => _logs;
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
Future<void> loadLogs() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_logs = await _repository.load();
|
||||
} catch (e) {
|
||||
debugPrint('Ошибка загрузки журнала инструктажей: $e');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addLog(InstructionLog log) async {
|
||||
try {
|
||||
final newLog = await _repository.add(log);
|
||||
_logs.add(newLog);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Ошибка добавления записи в журнал: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateLog(InstructionLog log) async {
|
||||
try {
|
||||
await _repository.update(log);
|
||||
final index = _logs.indexWhere((l) => l.id == log.id);
|
||||
if (index != -1) {
|
||||
_logs[index] = log;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Ошибка обновления записи в журнале: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteLog(String? id) async {
|
||||
try {
|
||||
await _repository.delete(id);
|
||||
_logs.removeWhere((log) => log.id == id);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Ошибка удаления записи из журнала: $e');
|
||||
}
|
||||
}
|
||||
|
||||
List<InstructionLog> getLogsByWorker(String workerId) {
|
||||
return _logs.where((log) => log.workerId == workerId).toList();
|
||||
}
|
||||
|
||||
List<InstructionLog> getLogsByInstruction(String instructionId) {
|
||||
return _logs.where((log) => log.instructionId == instructionId).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import '../data/interface/IRepository.dart';
|
||||
import '../models/worker.dart';
|
||||
|
||||
class WorkerProvider extends ChangeNotifier {
|
||||
final Repository<Worker> _repository;
|
||||
List<Worker> _workers = [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
WorkerProvider(this._repository);
|
||||
|
||||
List<Worker> get workers => _workers;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
|
||||
Future<void> loadWorkers() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_workers = await _repository.load();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addWorker(Worker worker) async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final newWorker = await _repository.add(worker);
|
||||
_workers.add(newWorker);
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateWorker(Worker worker) async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
await _repository.update(worker);
|
||||
final index = _workers.indexWhere((w) => w.id == worker.id);
|
||||
if (index != -1) {
|
||||
_workers[index] = worker;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteWorker(String? id) async {
|
||||
if (id == null) return;
|
||||
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
await _repository.delete(id);
|
||||
_workers.removeWhere((worker) => worker.id == id);
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/instruction_provider.dart';
|
||||
|
||||
class AddInstructionPage extends StatefulWidget {
|
||||
const AddInstructionPage({super.key});
|
||||
|
||||
@override
|
||||
State<AddInstructionPage> createState() => _AddInstructionPageState();
|
||||
}
|
||||
|
||||
class _AddInstructionPageState extends State<AddInstructionPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _titleController = TextEditingController();
|
||||
final _contentController = TextEditingController();
|
||||
final _tagController = TextEditingController();
|
||||
|
||||
final List<String> _tags = [];
|
||||
|
||||
void _addTag() {
|
||||
final tag = _tagController.text.trim().toLowerCase();
|
||||
if (tag.isNotEmpty && !_tags.contains(tag)) {
|
||||
setState(() {
|
||||
_tags.add(tag);
|
||||
_tagController.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _removeTag(String tag) {
|
||||
setState(() {
|
||||
_tags.remove(tag);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _saveInstruction() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final provider = Provider.of<InstructionProvider>(context, listen: false);
|
||||
await provider.addInstruction(
|
||||
_titleController.text,
|
||||
_contentController.text,
|
||||
_tags,
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_titleController.dispose();
|
||||
_contentController.dispose();
|
||||
_tagController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Новый инструктаж'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.save),
|
||||
onPressed: _saveInstruction,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _titleController,
|
||||
decoration: const InputDecoration(labelText: 'Название'),
|
||||
validator: (value) => (value == null || value.isEmpty) ? 'Введите название' : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _contentController,
|
||||
decoration: const InputDecoration(labelText: 'Содержание'),
|
||||
maxLines: 5,
|
||||
validator: (value) => (value == null || value.isEmpty) ? 'Введите содержание' : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _tagController,
|
||||
decoration: const InputDecoration(labelText: 'Добавить тег'),
|
||||
onSubmitted: (_) => _addTag(),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
onPressed: _addTag,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 8.0,
|
||||
children: _tags.map((tag) => Chip(
|
||||
label: Text(tag),
|
||||
onDeleted: () => _removeTag(tag),
|
||||
)).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/screens/instruction_page/instruction_list_page.dart';
|
||||
import 'package:instruction_app/screens/worker_page/list_workers_page.dart';
|
||||
import 'package:instruction_app/screens/organization_page/list_organization_page.dart';
|
||||
import 'package:instruction_app/screens/instruction_log_page/instruction_log_list_page.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
int _selectedIndex = 0;
|
||||
|
||||
static final List<Widget> _pages = [
|
||||
const InstructionListPage(),
|
||||
const ListWorkersPage(),
|
||||
const OrganizationListPage(),
|
||||
const InstructionLogListPage()
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: _pages[_selectedIndex],
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
currentIndex: _selectedIndex,
|
||||
onTap: (index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
},
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.description),
|
||||
label: 'Инструктажи',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.people),
|
||||
label: 'Работники',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.business),
|
||||
label: 'Организации',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.assignment),
|
||||
label: 'Журнал',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/instruction_log.dart';
|
||||
import '../../models/worker.dart';
|
||||
import '../../models/instruction.dart';
|
||||
import '../../providers/instruction_log_provider.dart';
|
||||
import '../../providers/worker_provider.dart';
|
||||
import '../../providers/instruction_provider.dart';
|
||||
|
||||
class InstructionLogFormPage extends StatefulWidget {
|
||||
final InstructionLog? log;
|
||||
|
||||
const InstructionLogFormPage({super.key, this.log});
|
||||
|
||||
@override
|
||||
State<InstructionLogFormPage> createState() => _InstructionLogFormPageState();
|
||||
}
|
||||
|
||||
class _InstructionLogFormPageState extends State<InstructionLogFormPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
String? _selectedWorkerId;
|
||||
String? _selectedInstructionId;
|
||||
DateTime _assignedDate = DateTime.now();
|
||||
InstructionStatus _status = InstructionStatus.assigned;
|
||||
DateTime? _completedDate;
|
||||
final _notesController = TextEditingController();
|
||||
|
||||
bool get _isEditing => widget.log != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (_isEditing) {
|
||||
final log = widget.log!;
|
||||
_selectedWorkerId = log.workerId;
|
||||
_selectedInstructionId = log.instructionId;
|
||||
_status = log.status;
|
||||
_notesController.text = log.notes ?? '';
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.read<WorkerProvider>().loadWorkers();
|
||||
context.read<InstructionProvider>().loadInstructions();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_notesController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? 'Редактировать запись' : 'Новая запись'),
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
),
|
||||
body: Consumer2<WorkerProvider, InstructionProvider>(
|
||||
builder: (context, workerProvider, instructionProvider, child) {
|
||||
final workers = workerProvider.workers;
|
||||
final instructions = instructionProvider.instructions;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
DropdownButtonFormField<String>(
|
||||
value: _selectedWorkerId,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Работник',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: workers.map((worker) {
|
||||
return DropdownMenuItem(
|
||||
value: worker.id,
|
||||
child: Text('${worker.name}'),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedWorkerId = value;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Выберите работника';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
DropdownButtonFormField<String>(
|
||||
value: _selectedInstructionId,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Инструктаж',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: instructions.map((instruction) {
|
||||
return DropdownMenuItem(
|
||||
value: instruction.id,
|
||||
child: Text(instruction.title),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedInstructionId = value;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Выберите инструктаж';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
ListTile(
|
||||
title: const Text('Дата назначения'),
|
||||
subtitle: Text(_formatDate(_assignedDate)),
|
||||
trailing: const Icon(Icons.calendar_today),
|
||||
onTap: () => _selectAssignedDate(context),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
DropdownButtonFormField<InstructionStatus>(
|
||||
value: _status,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Статус',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: InstructionStatus.values.map((status) {
|
||||
return DropdownMenuItem(
|
||||
value: status,
|
||||
child: Text(status.displayName),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_status = value!;
|
||||
if (_status != InstructionStatus.completed) {
|
||||
_completedDate = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
if (_status == InstructionStatus.completed)
|
||||
ListTile(
|
||||
title: const Text('Дата завершения'),
|
||||
subtitle: Text(_completedDate != null
|
||||
? _formatDate(_completedDate!)
|
||||
: 'Не выбрана'),
|
||||
trailing: const Icon(Icons.calendar_today),
|
||||
onTap: () => _selectCompletedDate(context),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
TextFormField(
|
||||
controller: _notesController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Примечания',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Отмена'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _saveLog,
|
||||
child: Text(_isEditing ? 'Сохранить' : 'Создать'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.day.toString().padLeft(2, '0')}.${date.month.toString().padLeft(2, '0')}.${date.year}';
|
||||
}
|
||||
|
||||
Future<void> _selectAssignedDate(BuildContext context) async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _assignedDate,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null && picked != _assignedDate) {
|
||||
setState(() {
|
||||
_assignedDate = picked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _selectCompletedDate(BuildContext context) async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _completedDate ?? DateTime.now(),
|
||||
firstDate: _assignedDate,
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
_completedDate = picked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _saveLog() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (_selectedWorkerId == null || _selectedInstructionId == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Заполните все обязательные поля')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_status == InstructionStatus.completed && _completedDate == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Укажите дату завершения')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final log = InstructionLog(
|
||||
id: _isEditing ? widget.log!.id : null,
|
||||
workerId: _selectedWorkerId!,
|
||||
instructionId: _selectedInstructionId!,
|
||||
status: _status,
|
||||
notes: _notesController.text.isEmpty ? null : _notesController.text,
|
||||
);
|
||||
|
||||
if (_isEditing) {
|
||||
context.read<InstructionLogProvider>().updateLog(log);
|
||||
} else {
|
||||
context.read<InstructionLogProvider>().addLog(log);
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/instruction_log.dart';
|
||||
import '../../models/worker.dart';
|
||||
import '../../models/instruction.dart';
|
||||
import '../../providers/instruction_log_provider.dart';
|
||||
import '../../providers/worker_provider.dart';
|
||||
import '../../providers/instruction_provider.dart';
|
||||
import 'instruction_log_form_page.dart';
|
||||
|
||||
class InstructionLogListPage extends StatefulWidget {
|
||||
const InstructionLogListPage({super.key});
|
||||
|
||||
@override
|
||||
State<InstructionLogListPage> createState() => _InstructionLogListPageState();
|
||||
}
|
||||
|
||||
class _InstructionLogListPageState extends State<InstructionLogListPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.read<InstructionLogProvider>().loadLogs();
|
||||
context.read<WorkerProvider>().loadWorkers();
|
||||
context.read<InstructionProvider>().loadInstructions();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Журнал инструктажей'),
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
),
|
||||
body: Consumer3<InstructionLogProvider, WorkerProvider, InstructionProvider>(
|
||||
builder: (context, logProvider, workerProvider, instructionProvider, child) {
|
||||
if (logProvider.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
final logs = logProvider.logs;
|
||||
final workers = workerProvider.workers;
|
||||
final instructions = instructionProvider.instructions;
|
||||
|
||||
if (logs.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('Журнал инструктажей пуст'),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: logs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final log = logs[index];
|
||||
final worker = workers.firstWhere(
|
||||
(w) => w.id == log.workerId,
|
||||
orElse: () => Worker(id: log.workerId, name: 'Неизвестный работник'),
|
||||
);
|
||||
final instruction = instructions.firstWhere(
|
||||
(i) => i.id == log.instructionId,
|
||||
orElse: () => Instruction(
|
||||
id: log.instructionId,
|
||||
title: 'Неизвестный инструктаж',
|
||||
content: '',
|
||||
tags: [],
|
||||
createdAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: ListTile(
|
||||
title: Text(worker.name),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Инструктаж: ${instruction.title}'),
|
||||
Text('Статус: ${log.status.displayName}'),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () => _editLog(context, log),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () => _deleteLog(context, log),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _addLog(context),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.day.toString().padLeft(2, '0')}.${date.month.toString().padLeft(2, '0')}.${date.year}';
|
||||
}
|
||||
|
||||
void _addLog(BuildContext context) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const InstructionLogFormPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _editLog(BuildContext context, InstructionLog log) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => InstructionLogFormPage(log: log),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _deleteLog(BuildContext context, InstructionLog log) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Подтверждение удаления'),
|
||||
content: const Text('Вы уверены, что хотите удалить эту запись из журнала?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Отмена'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.read<InstructionLogProvider>().deleteLog(log.id);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Удалить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../models/instruction.dart';
|
||||
import '../providers/instruction_provider.dart';
|
||||
import '../widgets/tag_selection_dialog.dart';
|
||||
import '/models/instruction.dart';
|
||||
import '/providers/instruction_provider.dart';
|
||||
import '/widgets/tag_selection_dialog.dart';
|
||||
|
||||
class AddEditInstructionPage extends StatefulWidget {
|
||||
final Instruction? initialInstruction;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../models/instruction.dart';
|
||||
import '../providers/instruction_provider.dart';
|
||||
import '/models/instruction.dart';
|
||||
import '/providers/instruction_provider.dart';
|
||||
import 'add_edit_instruction_page.dart';
|
||||
|
||||
class InstructionDetailPage extends StatelessWidget {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/instruction_provider.dart';
|
||||
import '/providers/instruction_provider.dart';
|
||||
import 'add_edit_instruction_page.dart';
|
||||
import 'instruction_detail_page.dart';
|
||||
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/worker.dart';
|
||||
import '../../providers/worker_provider.dart';
|
||||
import 'worker_form_page.dart';
|
||||
|
||||
class ListWorkersPage extends StatefulWidget {
|
||||
const ListWorkersPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ListWorkersPage> createState() => _ListWorkersPageState();
|
||||
}
|
||||
|
||||
class _ListWorkersPageState extends State<ListWorkersPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Load workers when the page is initialized
|
||||
Future.microtask(() =>
|
||||
Provider.of<WorkerProvider>(context, listen: false).loadWorkers()
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Список работников'),
|
||||
),
|
||||
body: Consumer<WorkerProvider>(
|
||||
builder: (context, workerProvider, child) {
|
||||
if (workerProvider.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (workerProvider.error != null) {
|
||||
return Center(child: Text('Ошибка: ${workerProvider.error}'));
|
||||
}
|
||||
|
||||
final workers = workerProvider.workers;
|
||||
|
||||
if (workers.isEmpty) {
|
||||
return const Center(child: Text('Список работников пуст'));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: workers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final worker = workers[index];
|
||||
return ListTile(
|
||||
title: Text(worker.name),
|
||||
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => WorkerFormPage(worker: worker),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
_showDeleteConfirmationDialog(context, worker);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
// Show worker details if needed
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const WorkerFormPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showDeleteConfirmationDialog(BuildContext context, Worker worker) async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: const Text('Подтверждение'),
|
||||
content: Text('Вы уверены, что хотите удалить работника "${worker.name}"?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Отмена'),
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('Удалить'),
|
||||
onPressed: () {
|
||||
Provider.of<WorkerProvider>(context, listen: false)
|
||||
.deleteWorker(worker.id);
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/worker.dart';
|
||||
import '../../providers/worker_provider.dart';
|
||||
|
||||
class WorkerFormPage extends StatefulWidget {
|
||||
final Worker? worker;
|
||||
|
||||
const WorkerFormPage({Key? key, this.worker}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<WorkerFormPage> createState() => _WorkerFormPageState();
|
||||
}
|
||||
|
||||
class _WorkerFormPageState extends State<WorkerFormPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late TextEditingController _nameController;
|
||||
late TextEditingController _positionController;
|
||||
late TextEditingController _departmentController;
|
||||
late TextEditingController _contactInfoController;
|
||||
|
||||
bool get _isEditing => widget.worker != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController = TextEditingController(text: widget.worker?.name ?? '');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_positionController.dispose();
|
||||
_departmentController.dispose();
|
||||
_contactInfoController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? 'Редактировать работника' : 'Добавить работника'),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'ФИО',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Пожалуйста, введите ФИО';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: _saveWorker,
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
child: Text(
|
||||
_isEditing ? 'Сохранить изменения' : 'Добавить работника',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _saveWorker() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final workerProvider = Provider.of<WorkerProvider>(context, listen: false);
|
||||
|
||||
final worker = Worker(
|
||||
id: widget.worker?.id,
|
||||
name: _nameController.text,
|
||||
);
|
||||
|
||||
if (_isEditing) {
|
||||
workerProvider.updateWorker(worker);
|
||||
} else {
|
||||
workerProvider.addWorker(worker);
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
137
pubspec.lock
137
pubspec.lock
|
|
@ -33,6 +33,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -54,6 +70,11 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.5"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -102,6 +123,46 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -110,6 +171,62 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
@ -171,6 +288,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
dart: ">=3.7.0 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ dependencies:
|
|||
http: ^0.13.6
|
||||
flutter_hooks: ^0.20.5
|
||||
provider: ^6.1.2 # Используем provider
|
||||
uuid: ^4.3.3
|
||||
uuid: ^4.3.3
|
||||
shared_preferences: ^2.5.3
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
Loading…
Reference in New Issue