- Рекомендуется: Visual Studio Code или Android Studio.
- Для VS Code: Установите расширения Flutter и Dart.
- Для Android Studio: Установите плагины Flutter и Dart через раздел Plugins.
Запустите команду:
flutter doctor
В командной строке создайте новый проект:
flutter create shopingapp
Перейдите в папку проекта:
cd shopingapp
Запустите приложение Flutter
flutter run
У вас будет список доступных устройств, на которых можно запустить приложение Flutter:
Linux (desktop): Ваше приложение будет запущено как настольное приложение для Linux.
Chrome (web): Ваше приложение будет запущено как веб-приложение в браузере Google Chrome.
выберите 1
Создаем папку screens, в ней создадим две страницы – главную страницу магазина(shop_screen.dart
) и страницу товара(device_screen.dart
)
shop_screen.dart
import 'package:flutter/material.dart';
class ShopScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Shop')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Navigate to the DeviceScreen
Navigator.pushNamed(context, '/device');
},
child: Text('Go to device screen'),
),
),
);
}
}
Обновите файл main.dart
:
import 'package:flutter/material.dart';
import 'screens/shop_screen.dart';
import 'screens/device_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shopping App',
initialRoute: '/shop',
routes: {
'/shop': (context) => ShopScreen(),
'/device': (context) => DeviceScreen(),
},
);
}
}
Структура каталога
lib/ screens/ shop_screen.dart device_screen.dart main.dart
Теперь
создаем store.dart
class AppState {
final List<Device> devices;
final Device? selectedDevice;
AppState({required this.devices, this.selectedDevice});
}
AppState описывает структуру состояния приложения: devices: Список всех доступных устройств. selectedDevice: Одно устройство, которое выбрано пользователем.
class SetDevicesAction {
final List<Device> devices;
SetDevicesAction(this.devices);
}
Экшен — это класс, описывающий действие, которое нужно выполнить с состоянием.
SetDevicesAction
:
Содержит список устройств devices, который будет установлен в глобальное состояние.
AppState appReducer(AppState state, dynamic action) {
if (action is SetDevicesAction) {
return AppState(devices: action.devices, selectedDevice: state.selectedDevice);
}
return state;
}
редюсер — функция, которая принимает текущее состояние (state) и экшен (action), а затем возвращает новое состояние. В данном случае:
Если получен экшен SetDevicesAction
, обновляется список устройств в состоянии.
В противном случае возвращается текущее состояние.
final store = Store<AppState>(
appReducer, // Основной редюсер.
initialState: AppState(devices: [], selectedDevice: null), // Начальное состояние.
);
Хранилище (Store): Объединяет: Редюсер (appReducer). Начальное состояние приложения (initialState). initialState: devices: Пустой список. selectedDevice: null.
import 'package:redux/redux.dart';
import 'package:shopingapp/models/device.dart';
class AppState {
final List<Device> devices;
final Device? selectedDevice;
AppState({required this.devices, this.selectedDevice});
}
class SetDevicesAction {
final List<Device> devices;
SetDevicesAction(this.devices);
}
AppState appReducer(AppState state, dynamic action) {
if (action is SetDevicesAction) {
return AppState(devices: action.devices, selectedDevice: state.selectedDevice);
}
return state;
}
final store = Store<AppState>(
appReducer,
initialState: AppState(devices: [], selectedDevice: null),
);
создаем модель устройства (Device), которая используется для представления данных об устройствах в приложении
class Device {
final int id;
final String title;
final String brand;
final double price;
final String image;
Device({
required this.id,
required this.title,
required this.brand,
required this.price,
required this.image,
});
factory Device.fromJson(Map<String, dynamic> json) {
return Device(
id: json['id'],
title: json['title'],
brand: json['brand'],
price: json['price'].toDouble(),
image: json['image'],
);
}
}
Обновите файл shop_screen.dart
:
-
ShopScreen
Основной виджет (StatelessWidget): Это главный экран, где отображается список устройств. Используется StoreConnector из пакета flutter_redux, чтобы получить данные из хранилища состояния (AppState).
StoreConnector<AppState, List<Device>>(
converter: (store) => store.state.devices,
converter: Функция, которая преобразует данные из Redux-хранилища в список устройств devices.
Добавление устройств по умолчанию (если список пуст):
if (devices.isEmpty) {
devices = [
Device(
id: 1,
title: 'Samsung s24 ultra',
price: 2000,
image: 'assets/images/device1.jpg',
brand: 'SAMSUNG',
),
...
];
}
Отображение списка устройств:
ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) {
final device = devices[index];
...
Переход на экран деталей устройства:
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DeviceDetailScreen(device: device),
),
);
},
child: Text('View Details'),
),
DeviceDetailScreen
Экран с подробностями устройства:
class DeviceDetailScreen extends StatelessWidget {
final Device device;
DeviceDetailScreen({required this.device});
Отображение деталей устройства:
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(device.image),
Text('Brand: ${device.brand}'),
Text('Price: \$${device.price}'),
],
),
),
shop_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:shopingapp/models/device.dart';
import 'package:shopingapp/store/store.dart';
class ShopScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, List<Device>>(
converter: (store) => store.state.devices,
builder: (context, devices) {
if (devices.isEmpty) {
devices = [
Device(
id: 1,
title: 'Samsung s24 ultra ',
price: 2000,
image: 'assets/images/device1.jpg',
brand: 'SAMSUNG',
),
Device(
id: 2,
title: 'Iphone 15 pro max',
price: 1000,
image: 'assets/images/device2.jpg',
brand: 'IPHONE',
),
];
}
return Scaffold(
appBar: AppBar(title: Text('Shop')),
body: ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) {
final device = devices[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
device.image,
height: 200,
fit: BoxFit.cover, //!!! Makes the image fit the size
),
),
SizedBox(height: 12),
Text(
device.title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 6),
Text('${device.price} USD'),
SizedBox(height: 12),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DeviceDetailScreen(device: device),
),
);
},
child: Text('View Details'),
),
],
),
),
),
);
},
),
);
},
);
}
}
class DeviceDetailScreen extends StatelessWidget {
final Device device;
DeviceDetailScreen({required this.device});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(device.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(device.image),
Text('Brand: ${device.brand}'),
Text('Price: \$${device.price}'),
],
),
),
);
}
}
Добавление зависимостей
Откройте файл pubspec.yaml и добавьте необходимые пакеты для работы приложения.
dependencies:
flutter:
sdk: flutter
flutter_redux: ^0.8.0
redux: ^4.0.0
provider: ^6.0.0
for pics
flutter:
assets:
- assets/images/
И конечно Обновите файл main.dart
:
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:shopingapp/store/store.dart';
import 'package:shopingapp/screens/shop_screen.dart';
import 'package:shopingapp/store/sotre.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(
home: ShopScreen(),
),
);
}
}
Структура каталога lib
── lib │ ├── main.dart │ ├── models │ │ └── device.dart │ ├── screens │ │ └── shop_screen.dart │ └── store │ └── store.dart
assets
├── assets │ └── images │ ├── device1.jpg │ └── device2.jpg
создаем cart_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:shopingapp/models/device.dart';
import 'package:shopingapp/store/store.dart';
class CartScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, List<Device>>(
converter: (store) => store.state.cart,
builder: (context, cart) {
return Scaffold(
appBar: AppBar(title: Text('Your Cart')),
body: cart.isEmpty
? Center(child: Text('Your cart is empty!'))
: ListView.builder(
itemCount: cart.length,
itemBuilder: (context, index) {
final device = cart[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 5,
child: ListTile(
leading: Image.asset(device.image, width: 50),
title: Text(device.title),
subtitle: Text('\$${device.price}'),
trailing: IconButton(
icon: Icon(Icons.remove_circle),
onPressed: () {
// Remove the device from the cart
StoreProvider.of<AppState>(context).dispatch(RemoveFromCartAction(device));
},
),
),
),
);
},
),
);
},
);
}
}
создаем device_detail_screen.dart
return Scaffold(
appBar: AppBar(title: Text(device.title)), // Заголовок с названием устройства.
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(device.image), // Картинка устройства.
Text('Brand: ${device.brand}'), // Бренд устройства.
Text('Price: \$${device.price}'), // Цена устройства.
AppBar: Отображает название устройства.
Image.asset: Показывает картинку устройства из локального хранилища (assets).
Text: Отображает бренд и цену устройства.
Добавление устройства в корзину
ElevatedButton(
onPressed: () {
StoreProvider.of<AppState>(context).dispatch(AddToCartAction(device));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${device.title} added to cart')),
);
},
child: Text('Add to Cart'),
),
Кнопка "Add to Cart":
При нажатии выполняет следующие действия:
Получает доступ к глобальному хранилищу Redux через StoreProvider.of(context).
Отправляет (dispatch) действие AddToCartAction(device), чтобы добавить устройство в корзину.
Показывает уведомление (SnackBar) с подтверждением.
Важно:
Для работы этой кнопки необходим экшен AddToCartAction и соответствующий редюсер, который добавляет устройство в корзину (их нужно реализовать в Redux-хранилище).
Кнопка "Back to Shop"
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back to Shop'),
),
Возвращает пользователя на предыдущий экран (в данном случае, на экран магазина).
Redux: Добавление устройства в корзину
Чтобы функциональность "добавления в корзину" работала, необходимо:
Создать экшен AddToCartAction:
class AddToCartAction {
final Device device;
AddToCartAction(this.device);
}
Добавить корзину (cart) в состояние AppState:
class AppState {
final List<Device> devices;
final List<Device> cart; // Корзина
final Device? selectedDevice;
AppState({
required this.devices,
required this.cart, // Инициализация корзины
this.selectedDevice,
});
}
Обновить редюсер (appReducer):
AppState appReducer(AppState state, dynamic action) {
if (action is AddToCartAction) {
return AppState(
devices: state.devices,
cart: List.from(state.cart)..add(action.device), // Добавляем устройство в корзину.
selectedDevice: state.selectedDevice,
);
}
return state;
}
import 'package:flutter/material.dart';
import 'package:shopingapp/models/device.dart';
import 'package:shopingapp/store/store.dart';
import 'package:flutter_redux/flutter_redux.dart';
class DeviceDetailScreen extends StatelessWidget {
final Device device;
DeviceDetailScreen({required this.device});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(device.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(device.image),
Text('Brand: ${device.brand}'),
Text('Price: \$${device.price}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
StoreProvider.of<AppState>(context).dispatch(AddToCartAction(device));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${device.title} added to cart')));
},
child: Text('Add to Cart'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back to Shop'),
),
],
),
),
);
}
}
Проверка окружения
Убедитесь, что у вас установлены все необходимые инструменты для сборки Android-приложений:
Android Studio (с установленным SDK).
Flutter SDK.
Правильно настроенные переменные окружения (flutter и adb должны быть доступны в терминале).
Проверьте работоспособность Flutter и Android SDK командой:
flutter doctor
Все пункты должны быть отмечены зелёным.
APK-файл для публикации нужно собирать в режиме release, чтобы исключить отладочные функции и улучшить производительность.
Выполните следующую команду:
flutter build apk --release
После успешной сборки APK будет находиться в папке:
build/app/outputs/flutter-apk/app-release.apk
Установите Django и настройте проект
pip install django
Создайте новый проект:
django-admin startproject auth_backend
cd auth_backend
Создайте приложение для аутентификации:
python manage.py startapp auth_app
Добавьте приложение в INSTALLED_APPS
в файле settings.py
:
```python
INSTALLED_APPS = [
...
'auth_app',
'rest_framework', # Для разработки API
]
```
Установите Django REST Framework
pip install djangorestframework
Добавьте Django REST Framework в INSTALLED_APPS
:
INSTALLED_APPS = [
...
'rest_framework',
]
auth_app/views.py
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
from django.contrib.auth import authenticate
import json
from .models import CustomUser
from django.contrib.auth import get_user_model
CustomUser = get_user_model()
@csrf_exempt
def register(request):
if request.method == "POST":
data = json.loads(request.body)
email = data.get("email")
password = data.get("password")
if CustomUser.objects.filter(username=email).exists():
return JsonResponse({"error": "User already exists"}, status=400)
# Save the user with plain_password and hashed password
user = CustomUser.objects.create_user(
username=email,
email=email,
password=password,
plain_password=password, # Save plain password for your specific need
)
return JsonResponse({"message": "User registered successfully"}, status=201)
return JsonResponse({"error": "Invalid method"}, status=405)
@csrf_exempt
def login_view(request):
if request.method == "POST":
data = json.loads(request.body)
email = data.get("email")
password = data.get("password")
user = authenticate(username=email, password=password)
if user is not None:
return JsonResponse({"message": "Login successful"}, status=200)
else:
return JsonResponse({"error": "Invalid credentials"}, status=400)
return JsonResponse({"error": "Invalid method"}, status=405)
auth_app/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register, name='register'),
path('login/', views.login_view, name='login'),
]
]
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
list_display = ('id', 'username', 'email', 'is_active')
search_fields = ('username', 'email')
ordering = ('id',)
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
plain_password = models.CharField(max_length=128, blank=True, null=True)
# Provide unique related_name values to avoid conflicts
groups = models.ManyToManyField(
'auth.Group',
related_name='customuser_groups', # Unique related name
blank=True,
)
user_permissions = models.ManyToManyField(
'auth.Permission',
related_name='customuser_permissions', # Unique related name
blank=True,
)
settings.py
INSTALLED_APPS
INSTALLED_APPS = [
...
'auth_app',
'rest_framework', # Для разработки API
]
в папку screens
создаем login_screen.dart
и register_screen.dart
login_screen.dart
import 'dart:convert'; // Для кодирования/декодирования JSON
import 'package:http/http.dart' as http; // Для выполнения HTTP-запросов
import 'package:flutter/material.dart'; // Для создания пользовательского интерфейса
cоздаем классы
class LoginScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
Метод build
@override
Widget build(BuildContext context)
Метод отвечает за построение пользовательского интерфейса.
Возвращает виджет Scaffold, который содержит структуру экрана с полем для ввода данных и кнопками.
Кнопка "Login"
ElevatedButton(
onPressed: () async {
String email = emailController.text.trim();
String password = passwordController.text.trim();
if (email.isNotEmpty && password.isNotEmpty) {
var response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/auth/login/'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': email,
'password': password,
}),
);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Login successful!')),
);
Navigator.pushReplacementNamed(context, '/shop');
} else {
var errorMessage = jsonDecode(response.body)['error'] ?? 'Invalid credentials';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage)),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in all fields')),
);
}
},
child: Text('Login'),
),
Функционал кнопки:
Собирает данные из текстовых полей emailController и passwordController.
Проверяет, что оба поля заполнены.
Отправляет POST-запрос на сервер (Django API) по адресу http://127.0.0.1:8000/api/auth/login/
.
В заголовках указывается, что данные отправляются в формате JSON.
Если сервер возвращает статус 200
, пользователь получает сообщение об успешной авторизации и перенаправляется на экран /shop.
Если сервер возвращает ошибку, показывается сообщение с текстом ошибки.
login_screen.dart
import 'dart:convert'; // For JSON encoding/decoding
import 'package:http/http.dart' as http; // For HTTP requests
import 'package:flutter/material.dart';
class LoginScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: emailController,
decoration: InputDecoration(labelText: 'Email'),
),
TextField(
controller: passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
// Retrieve user input
String email = emailController.text.trim();
String password = passwordController.text.trim();
if (email.isNotEmpty && password.isNotEmpty) {
// Send API request to backend
var response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/auth/login/'), // Django API endpoint
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': email,
'password': password,
}),
);
if (response.statusCode == 200) {
// Successful login
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Login successful!')),
);
Navigator.pushReplacementNamed(context, '/shop'); // Navigate to Shop Screen
} else {
// Handle error response
var errorMessage = jsonDecode(response.body)['error'] ?? 'Invalid credentials';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage)),
);
}
} else {
// Show validation error
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in all fields')),
);
}
},
child: Text('Login'),
),
TextButton(
onPressed: () {
Navigator.pushNamed(context, '/register'); // Navigate to Register Screen
},
child: Text('Don\'t have an account? Register here'),
),
],
),
),
);
}
}
register_screen.dart
создаем класс
class RegisterScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
RegisterScreen
— виджет, который отображает экран регистрации.
emailController
и passwordController
— контроллеры для полей ввода email и пароля, соответственно.
Метод build
@override
Widget build(BuildContext context)
Метод build
строит пользовательский интерфейс.
Возвращается виджет Scaffold
, который содержит все элементы на экране.
register_screen.dart
ElevatedButton(
onPressed: () async {
String email = emailController.text.trim();
String password = passwordController.text.trim();
if (email.isNotEmpty && password.isNotEmpty) {
try {
var response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/auth/register/'), // Обновите URL, если нужно
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'email': email, 'password': password}),
);
if (response.statusCode == 201) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Account created successfully!')),
);
Navigator.pushReplacementNamed(context, '/login');
} else {
var errorMessage = jsonDecode(response.body)['error'] ?? 'Registration failed';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage)),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Something went wrong: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in all fields')),
);
}
},
child: Text('Register'),
),
Функциональность кнопки "Register":
Когда кнопка нажата, происходит следующее:
Собираются значения из полей emailController и passwordController.
Проверяется, что оба поля заполнены.
Если поля заполнены, отправляется POST-запрос на сервер для создания аккаунта. URL запроса: http://127.0.0.1:8000/api/auth/register/
.
В заголовках указывается, что данные отправляются в формате JSON.
Если регистрация успешна (статус 201
), показывается сообщение о успешной регистрации и пользователь перенаправляется на экран авторизации (/login
).
Если произошла ошибка (например, неправильные данные или уже существует аккаунт), выводится соответствующее сообщение об ошибке.
В случае исключения (например, проблемы с интернет-соединением) также показывается сообщение об ошибке.
import 'dart:convert'; // For encoding/decoding JSON
import 'package:http/http.dart' as http; // For making HTTP requests
import 'package:flutter/material.dart';
class RegisterScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Register'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: emailController,
decoration: InputDecoration(labelText: 'Email'),
),
TextField(
controller: passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
String email = emailController.text.trim();
String password = passwordController.text.trim();
if (email.isNotEmpty && password.isNotEmpty) {
try {
var response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/auth/register/'), // Update URL if needed
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'email': email, 'password': password}),
);
if (response.statusCode == 201) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Account created successfully!')),
);
Navigator.pushReplacementNamed(context, '/login');
} else {
var errorMessage = jsonDecode(response.body)['error'] ?? 'Registration failed';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage)),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Something went wrong: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in all fields')),
);
}
},
child: Text('Register'),
),
],
),
),
);
}
}
структура lib
lib/
├── main.dart
├── screens/
│ ├── login_screen.dart
│ ├── register_screen.dart