Рубрики
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 вы можете создавать приложения с чистым кодом, который легко понимать и поддерживать.

Рубрики
Flutter

Flutter Observatory

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

Flutter Observatory доступен через веб-браузер и работает с любым устройством, на котором запущено Flutter-приложение. Для доступа к Observatory необходимо запустить приложение в режиме дебага и выполнить команду «flutter run» в терминале.

Некоторые из функций Flutter Observatory включают в себя:

  1. CPU Profiler: CPU Profiler позволяет анализировать использование процессора вашим приложением и находить места в коде, которые могут вызывать задержки (jank) или другие проблемы производительности. Он предоставляет информацию о времени выполнения каждого метода и вызове функций.
  2. Memory Profiler: Memory Profiler позволяет анализировать использование памяти в вашем приложении. Он отслеживает количество памяти, используемой каждым объектом в вашем приложении и помогает находить места, где происходят утечки памяти.
  3. Timeline: Timeline представляет собой инструмент для анализа производительности, который позволяет разработчикам получить общее представление о том, как работает их приложение. Он показывает временную линию всех событий в вашем приложении, включая рендеринг, анимации и взаимодействие с пользователем.
  4. Heap Snapshot: Heap Snapshot — это снимок памяти вашего приложения в определенный момент времени. Он позволяет разработчикам анализировать использование памяти и находить места, где происходят утечки памяти.

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