Added authorization page and page with a list of organizations
This commit is contained in:
parent
2a26c396bd
commit
c21a43190f
|
|
@ -0,0 +1,6 @@
|
|||
abstract class Repository<T> {
|
||||
Future<List<T>> load();
|
||||
Future<T> add(T organization);
|
||||
Future<void> update(T organization);
|
||||
Future<void> delete(String? id);
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import 'package:instruction_app/data/interface/IRepository.dart';
|
||||
import 'package:instruction_app/models/organization.dart';
|
||||
|
||||
class LocalOrganizationRepository implements Repository<Organization> {
|
||||
final List<Organization> _organizations = [
|
||||
Organization(
|
||||
id: '1',
|
||||
title: 'Про-Эксперт'
|
||||
),
|
||||
Organization(
|
||||
id: '2',
|
||||
title:'Лидер'
|
||||
),
|
||||
Organization(
|
||||
id: '3',
|
||||
title: 'Авангард'
|
||||
),
|
||||
Organization(
|
||||
id: '4',
|
||||
title: 'Галактика'
|
||||
)
|
||||
];
|
||||
|
||||
@override
|
||||
Future<List<Organization>> load() async {
|
||||
return List.of(_organizations);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Organization> add(Organization organization) async {
|
||||
// изменить id
|
||||
_organizations.add(organization);
|
||||
return organization;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(Organization organization) async {
|
||||
var index = _organizations.indexWhere((item) => item.id == organization.id);
|
||||
if (index != -1) {
|
||||
_organizations[index] = organization;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String? id) async {
|
||||
if (id == null)
|
||||
return;
|
||||
_organizations.removeWhere((item) => item.id == id);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
// lib/main.dart
|
||||
|
||||
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/models/organization.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:provider/provider.dart';
|
||||
import 'data/instruction_repository.dart';
|
||||
import 'providers/instruction_provider.dart';
|
||||
|
|
@ -11,10 +17,13 @@ void main() {
|
|||
final InstructionRepository repository = LocalInstructionRepository();
|
||||
//LocalInstructionRepository();
|
||||
|
||||
// final Repository<Organization> organizationRepository = LocalOrganizationRepository();
|
||||
|
||||
runApp(
|
||||
// "Предоставляем" наш провайдер всему дереву виджетов
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => InstructionProvider(repository),
|
||||
// create: (context) => OrganizationProvider(organizationRepository),
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
|
|
@ -31,7 +40,7 @@ class MyApp extends StatelessWidget {
|
|||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const InstructionListPage(),
|
||||
home: const AuthorizationPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
class Organization {
|
||||
final String? id;
|
||||
final String title;
|
||||
|
||||
Organization({
|
||||
this.id,
|
||||
required this.title
|
||||
});
|
||||
|
||||
Organization copyWith(String? id, String? title) {
|
||||
return Organization(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title
|
||||
);
|
||||
}
|
||||
|
||||
factory Organization.fromJson(Map<String, dynamic> json) {
|
||||
return Organization(
|
||||
id: json['id'],
|
||||
title: json['title'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'title': title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/data/interface/IRepository.dart';
|
||||
import 'package:instruction_app/models/organization.dart';
|
||||
|
||||
class OrganizationProvider extends ChangeNotifier {
|
||||
final Repository<Organization> _repository;
|
||||
|
||||
OrganizationProvider(this._repository);
|
||||
|
||||
List<Organization> _organizations = [];
|
||||
List<Organization> get organizations => _organizations;
|
||||
|
||||
bool _isLoading = false;
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
Future<void> loadOrganizations() async {
|
||||
setLoading(false);
|
||||
_organizations = await _repository.load();
|
||||
setLoading(true);
|
||||
}
|
||||
|
||||
Future<void> addOrganization(String title) async {
|
||||
final newOrganization = Organization(
|
||||
title: title
|
||||
);
|
||||
await _repository.add(newOrganization);
|
||||
await loadOrganizations();
|
||||
}
|
||||
|
||||
Future<void> updateOrganization(Organization organization) async {
|
||||
await _repository.update(organization);
|
||||
await loadOrganizations();
|
||||
}
|
||||
|
||||
Future<void> deleteOrganization(String id) async {
|
||||
await _repository.delete(id);
|
||||
_organizations.removeWhere((item) => item.id == id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setLoading(bool value){
|
||||
_isLoading = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class AuthorizationPage extends StatefulWidget {
|
||||
const AuthorizationPage({super.key});
|
||||
|
||||
@override
|
||||
State<AuthorizationPage> createState() => _AuthorizationPage();
|
||||
}
|
||||
|
||||
class _AuthorizationPage extends State<AuthorizationPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Авторизация"),
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.all(80),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
FormWidget(),
|
||||
SizedBox(height: 25,),
|
||||
TextButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.blue),
|
||||
foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text("Войти")
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FormWidget extends StatefulWidget {
|
||||
const FormWidget({super.key});
|
||||
|
||||
@override
|
||||
State<FormWidget> createState() => _FormWidget();
|
||||
}
|
||||
|
||||
class _FormWidget extends State<FormWidget> {
|
||||
final borederRadius = BorderRadius.all(Radius.circular(10));
|
||||
bool passwordVisible = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(Icons.account_circle),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: borederRadius
|
||||
),
|
||||
labelText: 'Логин'
|
||||
)
|
||||
),
|
||||
SizedBox(height: 25,),
|
||||
TextField(
|
||||
obscureText: !passwordVisible,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(Icons.lock),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: borederRadius
|
||||
),
|
||||
labelText: 'Пароль',
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
passwordVisible ? Icons.visibility : Icons.visibility_off
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
passwordVisible = !passwordVisible;
|
||||
});
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/models/organization.dart';
|
||||
import 'package:instruction_app/providers/organization_ptovider.dart';
|
||||
import 'package:instruction_app/screens/organization_page/edit_organization_page.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
class OrganizationDetailPage extends StatefulWidget {
|
||||
final Organization initialOrganization;
|
||||
const OrganizationDetailPage({super.key, required this.initialOrganization});
|
||||
|
||||
@override
|
||||
State<OrganizationDetailPage> createState() => _OrganizationDetailPage();
|
||||
}
|
||||
|
||||
class _OrganizationDetailPage extends State<OrganizationDetailPage>{
|
||||
late Organization editOrganization = widget.initialOrganization;
|
||||
|
||||
void _deleteOrganization(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Подтверждение операции'),
|
||||
content: Text("Вы уверены, что хотите удалить организацию \"${editOrganization.title}\"?"),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('Отмена')),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.read<OrganizationProvider>().deleteOrganization(editOrganization.id!);
|
||||
Navigator.of(ctx).pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Удалить', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(editOrganization.title, overflow: TextOverflow.ellipsis),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
tooltip: 'Редактировать',
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (ctx) => OrganizationAddPage(initialOrganization: editOrganization)))
|
||||
.then((value) => {
|
||||
setState(() {
|
||||
if (value != null && value is Organization)
|
||||
editOrganization = value;
|
||||
})
|
||||
}),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
tooltip: 'Удалить',
|
||||
onPressed: () => _deleteOrganization(context),
|
||||
),
|
||||
]
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: ListView(
|
||||
children: [
|
||||
Text('Содержание:',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Text('Описание ...',
|
||||
style: const TextStyle(fontSize: 16, height: 1.5)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/models/organization.dart';
|
||||
import 'package:instruction_app/providers/organization_ptovider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
class OrganizationAddPage extends StatefulWidget {
|
||||
final Organization? initialOrganization;
|
||||
const OrganizationAddPage({super.key, this.initialOrganization});
|
||||
|
||||
@override
|
||||
State<OrganizationAddPage> createState() => _OrganizationAddPage();
|
||||
}
|
||||
|
||||
class _OrganizationAddPage extends State<OrganizationAddPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late TextEditingController _titleController;
|
||||
|
||||
bool get _isEditing => widget.initialOrganization != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_titleController = TextEditingController(text: widget.initialOrganization?.title ?? '');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_titleController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _saveForm() async {
|
||||
if(_formKey.currentState!.validate()) {
|
||||
final provider = context.read<OrganizationProvider>();
|
||||
final title = _titleController.text;
|
||||
final updateOrganization = new Organization(
|
||||
id: widget.initialOrganization?.id,
|
||||
title: title
|
||||
);
|
||||
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// barrierDismissible: false,
|
||||
// builder: (context) => Center(child: CircularProgressIndicator())
|
||||
// );
|
||||
|
||||
try {
|
||||
if(_isEditing) {
|
||||
await provider.updateOrganization(updateOrganization);
|
||||
} else {
|
||||
await provider.addOrganization(title);
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop(updateOrganization);
|
||||
}
|
||||
} catch(exeption) {
|
||||
if(mounted) {
|
||||
// Navigator.of(context).pop(updateOrganization);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Ошибка сохранения: $exeption')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? 'Редактировать' : 'Добавление организации'),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'Сохранить',
|
||||
icon: const Icon(Icons.save),
|
||||
onPressed: _saveForm
|
||||
)],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _titleController,
|
||||
decoration: const InputDecoration(labelText: 'Название', border: OutlineInputBorder()),
|
||||
validator: (v) => v == null || v.isEmpty ? 'Название не может быть пустым' : null,
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:instruction_app/providers/organization_ptovider.dart';
|
||||
import 'package:instruction_app/screens/organization_page/edit_organization_page.dart';
|
||||
import 'package:instruction_app/screens/organization_page/detail_organization_page.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class OrganizationListPage extends StatefulWidget {
|
||||
const OrganizationListPage({super.key});
|
||||
|
||||
@override
|
||||
State<OrganizationListPage> createState() => _OrganizationListPage();
|
||||
}
|
||||
|
||||
class _OrganizationListPage extends State<OrganizationListPage>{
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() =>
|
||||
Provider.of<OrganizationProvider>(context, listen: false)
|
||||
.loadOrganizations());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Список организаций"),
|
||||
),
|
||||
body: Consumer<OrganizationProvider>(
|
||||
builder: (context, provider, child) {
|
||||
return Column(
|
||||
children: [
|
||||
provider.isLoading ?
|
||||
_widgetOrganizationList(provider) :
|
||||
const Expanded(child: Center(child: CircularProgressIndicator()))
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
// возможно поменять
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const OrganizationAddPage()),
|
||||
);
|
||||
},
|
||||
tooltip: "Добавить организацию",
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _widgetOrganizationList(OrganizationProvider provider){
|
||||
if (!provider.isLoading) {
|
||||
return const Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Организации не найдены',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: provider.organizations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final organization = provider.organizations[index];
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8
|
||||
),
|
||||
child: ListTile(
|
||||
title: Text(organization.title),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (ctx) => OrganizationDetailPage(initialOrganization: organization),
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue