Рубрики
Flutter

Использование JWT токенов во Flutter

JWT (JSON Web Token) — это открытый стандарт для безопасного обмена данными между двумя сторонами. Он состоит из трех частей: заголовка, полезной нагрузки и подписи.

Flutter — это фреймворк для мобильных приложений, который используется для создания кроссплатформенных приложений для iOS и Android.

Шаг 1: Генерация JWT токена

Первым шагом необходимо сгенерировать JWT токен на сервере. Токен может содержать информацию о пользователе, срок его действия и другую полезную информацию. Для генерации токена необходимо использовать библиотеку, которая поддерживает стандарт JWT.

Пример кода на сервере:

import 'package:jwt/json_web_token.dart';

String generateToken() {
  final claimSet = JwtClaim(
    issuer: 'example.com',
    subject: 'userid',
    audience: <String>['example.com'],
    jwtId: 'id',
    otherClaims: <String, dynamic>{
      'typ': 'authnresponse',
      'pld': {'k': 'v'}
    },
  );
  return issueJwtHS256(claimSet, 'secretKey');
}

Шаг 2: Сохранение JWT токена

После генерации токена, его необходимо сохранить на клиентской стороне. В Flutter для сохранения токена можно использовать пакет flutter_secure_storage, который позволяет сохранять данные безопасно на устройстве.

Пример кода на клиенте:

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

Future<void> saveToken(String token) async {
  await storage.write(key: 'jwt_token', value: token);
}

Шаг 3: Отправка JWT токена на сервер

Для отправки токена на сервер необходимо использовать HTTP-запросы. В Flutter для этого можно использовать пакет http, который позволяет отправлять GET, POST, PUT, DELETE запросы. В запросе необходимо передать токен в заголовке Authorization.

Пример кода на клиенте:

import 'package:http/http.dart' as http;

Future<void> sendToken(String token) async {
  final url = Uri.parse('<https://example.com/api>');
  final response = await http.post(url, headers: {'Authorization': 'Bearer $token'});
  print(response.body);
}

Шаг 4: Восстановление сессии

После отправки токена на сервер, сервер должен его проверить и установить сессию для пользователя. Если токен действителен, то сервер может использовать информацию, содержащуюся в нем, для авторизации пользователя и восстановления сессии.

Пример кода на сервере:

import 'package:jwt/json_web_token.dart';

void validateToken(String token) {
  final parts = token.split('.');
  final jwt = JsonWebToken.unverified(parts[0], parts[1], parts[2]);
  final claims = jwt.claims;
  // Validate claims
}

Шаг 5: Обработка ошибок

При использовании JWT токенов необходимо обрабатывать ошибки, связанные с их истечением. Если токен истек, то необходимо запросить новый токен и повторить запрос на сервер.

Пример кода на клиенте:

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

Future<String> getToken() async {
  final token = await storage.read(key: 'jwt_token');
  if (token == null) {
    throw Exception('Token not found');
  }
  // Check if token is expired
  return token;
}

Заключение

JWT токены и восстановление сессий являются важными компонентами безопасности в приложениях на Flutter. Их использование позволяет обеспечить безопасность пользовательских данных и защитить приложение от злоумышленников.

Рубрики
Flutter

Работа с JWT токеном в Dart

JWT Token

JWT, или JSON Web Token, это стандарт открытой авторизации, который позволяет передавать информацию между двумя сторонами в формате JSON. JWT токены используются для проверки подлинности клиента в защищенной среде.

Как работает JWT токен?

JWT токен состоит из трех частей: заголовок, полезная нагрузка и подпись. Заголовок содержит информацию о типе токена и алгоритме шифрования. Полезная нагрузка содержит информацию о пользователе, например, идентификатор и имя. Подпись создается с использованием секретного ключа, который хранится на сервере.

Когда пользователь авторизуется на сайте, сервер генерирует JWT токен, который затем отправляется клиенту. Клиент в свою очередь включает токен в каждый запрос, который отправляется на сервер. Сервер проверяет подпись токена, чтобы убедиться, что он был создан сервером и не был подделан. Если токен действителен, сервер разрешает доступ к защищенным ресурсам.

Преимущества JWT токена

JWT токены имеют несколько преимуществ перед традиционными методами авторизации, такими как использование сессий. Они могут быть переданы через URL или заголовок, что делает их более удобными для использования в API. Также они не требуют хранения на сервере, что уменьшает нагрузку на систему, и позволяет легко масштабировать приложение.

Пример использования JWT токена в Dart

Для работы с JWT токенами в Dart можно использовать библиотеку jwt_decode(https://pub.dev/packages/jwt_decode).

Для начала необходимо установить данную библиотеку. Для этого нужно в файле pubspec.yaml добавить следующий код:

dependencies:
  jwt_decode: ^0.1.1

После этого нужно запустить команду flutter packages get, чтобы скачать и установить библиотеку.

Для создания JWT токена можно использовать библиотеку jwt (https://pub.dev/packages/jwt). Вот пример кода, который генерирует JWT токен:

import 'package:jwt/jwt.dart';

var builder = new JWTBuilder();
builder
  ..issuer = '<https://example.com>'
  ..expiresAt = new DateTime.now().add(new Duration(minutes: 10))
  ..setClaim('data', {'userId': '123', 'userName': 'John Doe'});

var token = builder.getSignedToken('secret');

print(token);

Этот код создает JWT токен с истекающим сроком действия в 10 минут и добавляет в него информацию о пользователе.

Чтобы проверить JWT токен, можно использовать следующий код:

import 'package:jwt_decode/jwt_decode.dart';

String token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

Map<String, dynamic> decodedToken = Jwt.parseJwt(token);
print(decodedToken);

Этот код декодирует JWT токен и выводит его содержимое в консоль.

Заключение

JWT токены — это простой и эффективный способ обеспечения безопасности в веб-приложениях. Они могут быть использованы на разных языках программирования, в том числе и на Dart.

Рубрики
Flutter

Вебсокеты во Flutter

Вебсокеты — это технология, которая позволяет установить постоянное двустороннее соединение между клиентом и сервером. Это позволяет серверу отправлять данные клиенту в режиме реального времени, без необходимости для клиента постоянно обновлять страницу. В Flutter можно использовать пакет web_socket_channel, который предоставляет инструменты для работы с вебсокетами.

Прежде чем начать использовать вебсокеты, необходимо установить соединение с сервером. Для этого можно использовать конструктор WebSocketChannel.connect из пакета web_socket_channel. Например:

import 'package:web_socket_channel/io.dart';

final channel = IOWebSocketChannel.connect('ws://localhost:1234');

Этот код устанавливает соединение с сервером по адресу ws://localhost:1234.

После установления соединения можно начать отправлять и получать данные с сервера. Для отправки данных можно использовать метод sink.add, а для получения данных — стрим stream.

channel.sink.add('Hello, server!');
channel.stream.listen((message) {
  print('Received: $message');
});

Этот код отправляет сообщение Hello, server! на сервер и выводит в консоль все сообщения, которые приходят с сервера.

Чтобы убедиться, что соединение остается открытым на протяжении суток, необходимо обрабатывать ошибки соединения и переподключаться к серверу в случае его отключения. Для этого можно использовать метод WebSocketChannel.sink.done, который возвращает Future, который выполнится, когда соединение будет закрыто. В обработчике этого Future можно производить повторное соединение с сервером. Например:

void startWebSocket() {
  final channel = IOWebSocketChannel.connect('ws://localhost:1234');
  channel.stream.listen((message) {
    print('Received: $message');
  }, onError: (error) {
    print('Error: $error');
    Future.delayed(Duration(seconds: 5), () => startWebSocket());
  }, onDone: () {
    print('Done!');
    Future.delayed(Duration(seconds: 5), () => startWebSocket());
  });
}

Этот код устанавливает соединение с сервером при помощи вебсокет и обрабатывает ошибки соединения и переподключается к серверу в случае его отключения.

Надеюсь, эта информация окажется полезной для вас!

Рубрики
Flutter

Как избежать перерисовок в Flutter с помощью Provider

Flutter является мощным фреймворком, который позволяет создавать красивые и быстрые мобильные приложения. Однако, при разработке приложений на Flutter может возникнуть проблема частых перерисовок экрана приложения. В этой статье мы рассмотрим, как избежать перерисовок в Flutter с помощью Provider.

Что такое перерисовки в Flutter?

Перерисовки в Flutter происходят, когда виджет перерисовывается на экране. Это может происходить во время изменения состояния приложения или при вызове метода setState(). Перерисовки могут замедлить работу приложения и потреблять большое количество ресурсов устройства.

Как избежать перерисовок в Flutter с помощью Provider?

Flutter Provider — это библиотека для управления состоянием в приложении. Она позволяет избежать перерисовок приложения путем использования Provider и Consumer. Рассмотрим несколько способов использования Provider для избежания перерисовок.

1. Использование Selector

Selector — это виджет, который позволяет выбирать только необходимые данные из Provider. Он позволяет избежать перерисовки всего экрана при изменении какого-то одного поля в Provider.

Для примера, предположим, что у нас есть класс Counter с полем count. Мы хотим отобразить значение поля count на экране. Для этого мы можем использовать Selector, чтобы избежать перерисовки всего экрана при изменении поля count:

class Counter {
  int count = 0;

  void increment() {
    count++;
  }
}

class CounterProvider with ChangeNotifier {
  Counter _counter = Counter();

  Counter get counter => _counter;

  void increment() {
    _counter.increment();
    notifyListeners();
  }
}

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Selector<CounterProvider, int>(
          selector: (_, provider) => provider.counter.count,
          builder: (_, count, __) => Text(count.toString()),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<CounterProvider>(context, listen: false).increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

2. Использование Consumer

Consumer — это виджет, который позволяет перерисовывать только ту часть экрана, которая изменилась. Он используется для обновления только той части экрана, которая зависит от состояния Provider.

Для примера, предположим, что у нас есть класс Task с полем title. Мы хотим отобразить список задач на экране. Когда пользователь добавляет новую задачу, мы хотим обновить только список задач, а не весь экран. Мы можем использовать Consumer для обновления только списка задач:

class Task {
  String title;

  Task(this.title);
}

class TaskProvider with ChangeNotifier {
  List<Task> _tasks = [];

  List<Task> get tasks => _tasks;

  void addTask(Task task) {
    _tasks.add(task);
    notifyListeners();
  }
}

class TaskScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: Consumer<TaskProvider>(
              builder: (_, provider, __) => ListView.builder(
                itemCount: provider.tasks.length,
                itemBuilder: (_, index) => ListTile(
                  title: Text(provider.tasks[index].title),
                ),
              ),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showDialog(
            context: context,
            builder: (_) => AlertDialog(
              title: Text('Add Task'),
              content: TextField(
                onChanged: (value) {
                  Provider.of<TaskProvider>(context, listen: false)
                      .addTask(Task(value));
                },
              ),
            ),
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

3. Использование ValueNotifier

ValueNotifier — это класс, который позволяет уведомлять слушателей только при изменении значения. Это позволяет избежать частых перерисовок, если изменения не влияют на отображение на экране.

Для примера, предположим, что у нас есть класс Theme с полем primaryColor. Мы хотим изменять цвет темы приложения без перерисовки всего экрана. Мы можем использовать ValueNotifier, чтобы избежать частых перерисовок:

class Theme {
  Color primaryColor;

  Theme(this.primaryColor);
}

class ThemeProvider with ChangeNotifier {
  ValueNotifier<Theme> _theme = ValueNotifier(Theme(Colors.blue));

  ValueNotifier<Theme> get theme => _theme;

  void changeThemeColor(Color color) {
    _theme.value = Theme(color);
  }
}

class ThemeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Consumer<ThemeProvider>(
          builder: (_, provider, __) => Container(
            width: 100,
            height: 100,
            color: provider.theme.value.primaryColor,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<ThemeProvider>(context, listen: false)
              .changeThemeColor(Colors.green);
        },
        child: Icon(Icons.color_lens),
      ),
    );
  }
}

Заключение

Использование Provider в Flutter позволяет избежать частых перерисовок экрана приложения и улучшить производительность приложения. Мы рассмотрели несколько способов использования Provider для избежания перерисовок, таких как Selector, Consumer и ValueNotifier. Надеемся, что эти советы помогут вам создать более бы

Рубрики
Flutter

Как использовать Flutter Provider для управления состоянием приложения

Flutter Provider — это библиотека для управления состоянием приложения в Flutter. Она помогает легко передавать данные между виджетами и управлять ими, минуя необходимость использования стейтфул-виджетов. В этой статье мы рассмотрим, как использовать Flutter Provider для управления состоянием приложения.

Шаг 1: Установка Flutter Provider

Первым шагом является установка Flutter Provider. Для этого добавьте следующую строку в ваш файл pubspec.yaml:

dependencies:
  provider: ^6.0.1

После этого запустите команду flutter packages get для установки Flutter Provider.

Шаг 2: Создание модели данных

Во втором шаге мы создадим модель данных для нашего приложения. В качестве примера, мы будем использовать список задач. Создайте новый файл task_model.dart и добавьте следующий код:

class TaskModel {
  final int id;
  final String title;
  final bool isDone;

  TaskModel({required this.id, required this.title, this.isDone = false});
}

Здесь мы создали простую модель TaskModel, которая содержит идентификатор, заголовок и флаг, указывающий, выполнена ли задача.

Шаг 3: Создание класса управления состоянием

Для управления состоянием приложения мы создадим класс TaskProvider, который будет содержать список задач и необходимые методы для управления ими. Создайте новый файл task_provider.dart и добавьте следующий код:

import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';
import 'task_model.dart';

class TaskProvider extends ChangeNotifier {
  List<TaskModel> _tasks = [
    TaskModel(id: 1, title: 'Купить молоко'),
    TaskModel(id: 2, title: 'Сходить в банк'),
    TaskModel(id: 3, title: 'Посетить спортзал'),
  ];

  List<TaskModel> get tasks => _tasks;

  void addTask(String title) {
    final newTask = TaskModel(id: _tasks.length + 1, title: title);
    _tasks.add(newTask);
    notifyListeners();
  }

  void toggleTask(int id) {
    final taskIndex = _tasks.indexWhere((task) => task.id == id);
    _tasks[taskIndex].isDone = !_tasks[taskIndex].isDone;
    notifyListeners();
  }

  void deleteTask(int id) {
    _tasks.removeWhere((task) => task.id == id);
    notifyListeners();
  }
}

Здесь мы создали класс TaskProvider, который содержит список задач _tasks и три метода для добавления, изменения и удаления задач. Обратите внимание на использование метода notifyListeners(). Он уведомляет все подписанные виджеты о необходимости обновления состояния.

Теперь мы объявим наш TaskProvider в виджете, чтобы использовать его для управления состоянием приложения. Для этого создадим корневой виджет MyApp, в котором мы обернем весь приложение в ChangeNotifierProvider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'task_provider.dart';
import 'task_screen.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => TaskProvider(),
      child: MaterialApp(
        title: 'Flutter Provider Demo',
        home: TaskScreen(),
      ),
    );
  }
}

Здесь мы использовали ChangeNotifierProvider, чтобы создать экземпляр TaskProvider и передать его в качестве значения create. Затем мы обернули наше приложение в MaterialApp и установили TaskScreen в качестве домашнего экрана.

Шаг 5: Использование Provider в виджетах

Теперь мы можем использовать TaskProvider в наших виджетах, чтобы управлять состоянием приложения. Для этого мы используем Provider.of<TaskProvider>(context) для получения экземпляра TaskProvider.

Например, в TaskScreen мы можем использовать Consumer<TaskProvider> для перерисовки только необходимых частей экрана при изменении состояния TaskProvider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'task_model.dart';
import 'task_provider.dart';

class TaskScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Мои задачи'),
      ),
      body: Consumer<TaskProvider>(
        builder: (context, taskProvider, child) => ListView.builder(
          itemCount: taskProvider.tasks.length,
          itemBuilder: (context, index) {
            final task = taskProvider.tasks[index];
            return CheckboxListTile(
              title: Text(task.title),
              value: task.isDone,
              onChanged: (value) {
                taskProvider.toggleTask(task.id);
              },
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final title = await _showTaskDialog(context);
          if (title != null && title.isNotEmpty) {
            Provider.of<TaskProvider>(context, listen: false).addTask(title);
          }
        },
        tooltip: 'Добавить задачу',
        child: Icon(Icons.add),
      ),
    );
  }

  Future<String?> _showTaskDialog(BuildContext context) {
    final controller = TextEditingController();
    return showDialog<String>(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Добавить задачу'),
        content: TextField(
          controller: controller,
          decoration: InputDecoration(hintText: 'Введите задачу'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Отмена'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, controller.text),
            child: Text('Добавить'),
          ),

Шаг 6: Изменение состояния Provider

Теперь мы можем обновлять состояние приложения, используя методы TaskProvider. Например, мы можем использовать addTask для добавления новой задачи:

Provider.of<TaskProvider>(context, listen: false).addTask(title);

Или мы можем использовать toggleTask для переключения состояния задачи:

taskProvider.toggleTask(task.id);

Заключение

В этой статье мы рассмотрели, как использовать Flutter Provider для управления состоянием приложения. Мы создали TaskProvider, который наследуется от ChangeNotifier, и используем его для хранения списка задач. Затем мы создали виджет TaskScreen, который использует Consumer<TaskProvider> для обновления состояния при изменении списка задач.

Flutter Provider является мощным инструментом для управления состоянием в Flutter-приложениях. Он позволяет создавать экземпляры классов Provider, которые могут использоваться для хранения и обновления состояния приложения в любом месте приложения. С помощью Provider вы можете создавать приложения с чистым кодом, который легко понимать и поддерживать.