Создаем Flutter-приложение для оплаты через СБП В предыдущем посте рассказали о главных шагах, теперь разберем их подробнее. Начинаем с настройки получения списка банков.
🔸Создаем объект для полей bankName, logoURL и schema. (Для безопасного парсинга данных рекомендуем использовать паттерн проектирования DTO):
```dart
class BankItem {
const BankItem({
required this.bankName,
required this.logoURL,
required this.schema,
});
final String bankName;
final String logoURL;
final String schema;
static BankItem? fromJson(Map<String, dynamic> json) {
final bankName = json['bankName'] as String?;
final logoURL = json['logoURL'] as String?;
final schema = json['schema'] as String?;
if (bankName == null || logoURL == null || schema == null) {
return null;
}
return BankItem(
bankName: utf8.decode(bankName.codeUnits),
logoURL: logoURL,
schema: schema,
);
}
}
```
*Здесь используем utf8 из-за проблем с кодировкой. Если у банка нет схемы, имени или картинки, не считаем его валидным.
🔸Создаем абстрактный репозиторий
SbpRepository для управления запросами СБП и реализуем метод
getBankList в
NewtorkSbpRepository, чтобы получить список банков-участников СБП:
class NewtorkSbpRepository implements SbpRepository {
Future<Iterable<BankItem>> getBankList() async {
try {
final response = await http.get(
Uri.parse('https://qr.nspk.ru/proxyapp/c2bmembers.json'),
);
final decodedMap = jsonDecode(response.body) as Map<String, dynamic>;
final rawBanks = decodedMap['dictionary'] as List;
final banks = rawBanks.map((e) => BankItem.fromJson(e)).whereNotNull();
return banks;
} catch (error, trace) {
/// Добавить обработку особых ошибок
rethrow;
}
}
}
Реализованный метод можно вызывать на любом этапе бизнес-логики, где нужно получить список банков.
Для управления состояниями можно использовать любой стейт-менеджер, который вам нравится (например flutter_bloc, riverpod).
Мы рассмотрим вариант без сторонних библиотек, используем FutureBuilder и InheritedWidget.
🔸Создаем экран для оплаты, который будет при инициализации запрашивать список банков из репозитория:
class SbpPayScreen extends StatefulWidget {
const SbpPayScreen({super.key});
@override
State<SbpPayScreen> createState() => _SbpPayScreenState();
}
class _SbpPayScreenState extends State<SbpPayScreen> {
late final Future<Iterable<BankItem>> bankList;
@override
void initState() {
bankList = SbpController.of(context).repository.getBankList();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Оплата СБП'),
),
body: FutureBuilder(
future: bankList,
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) => ListTile(
title: Text(data.elementAt(index).bankName),
),
);
}
if (snapshot.hasError) {
return const Text('Произошла ошибка');
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}