{"id":380134,"date":"2024-06-27T15:00:45","date_gmt":"2024-06-27T15:00:45","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=380134"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=380134","title":{"rendered":"<span>\u0421\u043e\u0437\u0434\u0430\u0435\u043c Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u043f\u043b\u0430\u0442\u044b \u0447\u0435\u0440\u0435\u0437 \u0421\u0411\u041f \u0431\u0435\u0437 \u043d\u0430\u0442\u0438\u0432\u0430<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/bc6\/00b\/b1c\/bc600bb1c138fdd21736e0891508c8b1.jpg\" width=\"1958\" height=\"1103\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bc6\/00b\/b1c\/bc600bb1c138fdd21736e0891508c8b1.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 <a href=\"https:\/\/habr.com\/ru\/users\/RonFall\/\"><u>\u041c\u0443\u0440\u0430\u0442 \u041d\u0430\u0441\u0438\u0440\u043e\u0432<\/u><\/a>, \u044f Flutter-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <a href=\"https:\/\/friflex.com\/flutter\/\"><u>\u0432 Friflex<\/u><\/a>. \u041c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0438 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 Flutter.\u00a0<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c, \u043a\u0430\u043a \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0421\u0411\u041f \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/sbp.nspk.ru\/file\/sbp_SDK.zip\"><u>\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u041d\u0421\u041f\u041a<\/u><\/a> (\u041d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u043b\u0430\u0442\u0435\u0436\u043d\u044b\u0445 \u043a\u0430\u0440\u0442). \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0432\u0443\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u0437 pub.dev.<\/p>\n<h3>\u0412\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443<\/h3>\n<p>\u041d\u0430 \u0441\u0430\u0439\u0442\u0435 API \u0421\u0411\u041f \u043e\u043f\u0438\u0441\u0430\u043d\u043e, \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u043b\u044f \u043e\u043f\u043b\u0430\u0442\u044b \u043f\u043e <a href=\"https:\/\/sbp.nspk.ru\/api\/#section\/Format-Funkcionalnoj-ssylki-SBP\"><u>\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0435<\/u><\/a>. \u041e\u0431\u044b\u0447\u043d\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u0434\u043b\u044f \u043e\u043f\u043b\u0430\u0442\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0430\u0439\u0442 \u0421\u0411\u041f, \u0433\u0434\u0435 \u0438\u0437 \u043d\u0435\u0435 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f QR-\u043a\u043e\u0434. \u0424\u043e\u0440\u043c\u0430\u0442 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0438:<\/p>\n<pre><code class=\"markdown\">https:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G? type=01&amp;bank=100000000007&amp;crc=0C8A<\/code><\/pre>\n<p>\u041d\u0435\u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e QR-\u043a\u043e\u0434\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0431\u0430\u043d\u043a\u0430. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043e\u043a\u043d\u043e \u043e\u043f\u043b\u0430\u0442\u044b \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u0440\u0438\u0435\u043c\u0449\u0438\u043a\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0430.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/1aa\/025\/5cd\/1aa0255cd5abe3e2c9e15d3d701a43e4.jpg\" width=\"1067\" height=\"600\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1aa\/025\/5cd\/1aa0255cd5abe3e2c9e15d3d701a43e4.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u0421\u0411\u041f, \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u044f \u0441\u0445\u0435\u043c\u0430. \u042d\u0442\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 id. \u041e\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u0432 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0421\u0411\u041f, \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/qr.nspk.ru\/proxyapp\/c2bmembers.json\"><u>\u0437\u0434\u0435\u0441\u044c.<\/u><\/a><\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043f\u043e\u043b\u0435 <code>schema<\/code> \u0438\u0437 \u0441\u0441\u044b\u043b\u043a\u0438 \u0432\u044b\u0448\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u0441\u0441\u044b\u043b\u043a\u0443 \u043e\u043f\u043b\u0430\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u043e <code>https<\/code>, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0434\u0438\u043f\u043b\u0438\u043d\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0441\u044b\u043b\u043a\u0430 \u0434\u043b\u044f \u0411\u0430\u043d\u043a\u0430 \u041e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"markdown\">bank100000000015:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G? type=01&amp;bank=100000000007&amp;crc=0C8A<\/code><\/pre>\n<p>\u0411\u0430\u043d\u043a\u0438, \u0447\u044c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u044b\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0438\u0437 App Store \u0438 Google Play, \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u043e\u043d\u044b. \u0423 \u043a\u043b\u043e\u043d\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f <code>package_name<\/code>, \u0430 \u043f\u043e\u043b\u0435 <code>schema<\/code> \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0443 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u0435\u0441\u043b\u0438 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0421\u0431\u0435\u0440\u0411\u0430\u043d\u043a \u0441\u0442\u043e\u0438\u0442 \u0430\u043d\u0430\u043b\u043e\u0433, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0423\u043c\u043d\u044b\u0439 \u041e\u043d\u043b\u0430\u0439\u043d, \u043e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e \u0421\u0411\u041f \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e.<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0414\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0448\u0430\u0433\u043e\u0432:<\/p>\n<p>1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <a href=\"https:\/\/qr.nspk.ru\/proxyapp\/c2bmembers.json\"><u>\u0441\u043f\u0438\u0441\u043e\u043a<\/u><\/a> \u0431\u0430\u043d\u043a\u043e\u0432.<\/p>\n<p>2. \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0442\u0440\u0435\u0445 \u043f\u043e\u043b\u0435\u0439: <code>bankName<\/code>, <code>logoURL<\/code> \u0438 <code>schema<\/code>.<\/p>\n<p>3. \u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u044d\u0442\u0438 \u043f\u043e\u043b\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432\u0441\u0435 \u0431\u0430\u043d\u043a\u0438 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435.<\/p>\n<p>4. \u0421\u0442\u0430\u0432\u0438\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0430\u0442\u0438\u044f (<code>GestureDetector<\/code>) \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0431\u0430\u043d\u043a. \u041c\u0435\u043d\u044f\u0435\u043c \u0432 \u0441\u0441\u044b\u043b\u043a\u0430\u0445 \u043e\u043f\u043b\u0430\u0442\u044b <code>https<\/code> \u043d\u0430 <code>schema<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0431\u0430\u043d\u043a\u0430.<\/p>\n<p>5. \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u043c \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>AppLifecycleListener<\/code>. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043a\u043e\u043b\u043b\u0431\u0435\u043a <code>onRestart<\/code>, \u0432 \u043d\u0435\u043c \u0441\u0442\u0430\u0432\u0438\u043c \u0444\u043b\u0430\u0433. \u0412\u0442\u043e\u0440\u043e\u0439 \u0444\u043b\u0430\u0433 \u0441\u0442\u0430\u0432\u0438\u043c \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430.<\/p>\n<p>6. \u041f\u043e\u0441\u043b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043a\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u043e\u043f\u043b\u0430\u0442\u044b.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0430 \u043f\u0430\u043a\u0435\u0442\u0430. \u0412 <code>pubspec.yaml<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c:<\/p>\n<pre><code class=\"yaml\">dependencies:   http: any   url_launcher: any<\/code><\/pre>\n<p>\u0412\u0435\u0440\u0441\u0438\u044e \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0430\u0448\u0435\u0433\u043e Flutter SDK. \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u0430\u043d\u043a\u043e\u0432. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439: <code>bankName<\/code>, <code>logoURL<\/code> \u0438 <code>schema<\/code>.<\/p>\n<pre><code class=\"dart\">class BankItem {   const BankItem({     required this.bankName,     required this.logoURL,     required this.schema,   });    final String bankName;   final String logoURL;   final String schema;    @override   bool operator ==(Object other) =>       identical(this, other) ||       other is BankItem &amp;&amp;           runtimeType == other.runtimeType &amp;&amp;           bankName == other.bankName &amp;&amp;           logoURL == other.logoURL &amp;&amp;           schema == other.schema;    @override   int get hashCode => bankName.hashCode ^ logoURL.hashCode ^ schema.hashCode; } <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432-\u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0421\u0411\u041f:  <\/p>\n<pre><code class=\"dart\">Future&lt;List&lt;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&lt;String, dynamic>;     final bankList = decodedMap['dictionary'] as List;     final mappedList = &lt;BankItem>[];      for (final item in bankList) {       final bankName = item['bankName'] as String?;       final logoURL = item['logoURL'] as String?;       final schema = item['schema'] as String?;       if (schema == null || logoURL == null || bankName == null) continue;       if (schema.isEmpty || logoURL.isEmpty || bankName.isEmpty) continue;       mappedList.add(         BankItem(           bankName: utf8.decode(bankName.codeUnits),           logoURL: logoURL,           schema: schema,         ),       );     }      return mappedList;   } on Object {     return &lt;BankItem>[];   } } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>utf8<\/code> \u0438\u0437-\u0437\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u043e\u0439. \u0415\u0441\u043b\u0438 \u0443 \u0431\u0430\u043d\u043a\u0430 \u043d\u0435\u0442 \u0441\u0445\u0435\u043c\u044b, \u0438\u043c\u0435\u043d\u0438 \u0438\u043b\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>FutureBuilder<\/code> \u0438 <code>ListView<\/code>.<\/p>\n<p>\u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u043e \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0438\u0437 \u043d\u0435\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>WidgetsBindingObserver<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>AppLifecycleListener.<\/code>\u00a0<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c <code>Map<\/code> \u0441 \u0434\u0432\u0443\u043c\u044f \u0444\u043b\u0430\u0433\u0430\u043c\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0434\u0432\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f: \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u043f\u0440\u0438 <code>onRestart<\/code>) \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430 \u043f\u043e \u0434\u0438\u043f\u043b\u0438\u043d\u043a\u0443. <code>Map<\/code> \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432 <code>ValueNotifier<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043e\u0431\u0430 \u0444\u043b\u0430\u0433\u0430 <code>true<\/code>.<\/p>\n<pre><code class=\"dart\">class _SbpPayScreenState extends State&lt;SbpPayScreen>     with WidgetsBindingObserver {   late final ValueNotifier&lt;Map&lt;String, bool>> _statesMapNotifier;   late final AppLifecycleListener _lifecycleListener;    @override   void initState() {     super.initState();     WidgetsBinding.instance.addObserver(this);     _statesMapNotifier = ValueNotifier&lt;Map&lt;String, bool>>(       {'wasRestarted': false, 'wasTransited': false},     );     _lifecycleListener = AppLifecycleListener(       onRestart: () {         _statesMapNotifier.value = {           'wasRestarted': true,           'wasTransited': _statesMapNotifier.value['wasTransited'] ??               false,         };       },     );     _statesMapNotifier.addListener(() {       final wasRestarted = _statesMapNotifier.value['wasRestarted'] ??           false;       final wasTransited = _statesMapNotifier.value['wasTransited'] ??           false;       if (wasRestarted &amp;&amp; wasTransited) {         Future.delayed(           const Duration(seconds: 4),               () {             _statesMapNotifier.value = {               'wasRestarted': false,               'wasTransited': false,             };           },         );       }     });   }    @override   void dispose() {     _lifecycleListener.dispose();     _statesMapNotifier.dispose();     WidgetsBinding.instance.removeObserver(this);     super.dispose();   }    ... } <\/code><\/pre>\n<p>\u0412 <code>Map<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0432\u0430 \u043a\u043b\u044e\u0447\u0430:\u00a0<\/p>\n<ul>\n<li>\n<p><code>wasRestarted<\/code> \u2014 \u0444\u043b\u0430\u0433, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0441\u0432\u0435\u0440\u043d\u0443\u0442\u043e \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u043e \u0441\u043d\u043e\u0432\u0430;<\/p>\n<\/li>\n<li>\n<p><code>wasTransited<\/code> \u2014 \u0444\u043b\u0430\u0433, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u043b \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430.\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0431\u0430 \u0444\u043b\u0430\u0433\u0430 \u0441\u0442\u0430\u043b\u0438 <code>true<\/code>, \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430. \u0427\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c, \u043e\u043f\u043b\u0430\u0442\u0438\u043b \u043b\u0438 \u043e\u043d, \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0443 \u0431\u044d\u043a\u0435\u043d\u0434\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443 <code>_statesMapNotifier<\/code> \u0441\u0442\u043e\u0438\u0442 \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u0441 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0435\u0439 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u044f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u0412 <code>build<\/code> \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <code>ValueListenableBuilder<\/code>. \u041e\u043d \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<\/p>\n<pre><code class=\"dart\">ValueListenableBuilder(     valueListenable: _statesMapNotifier,     builder: (_, statesMap, __) {       final wasRestarted = statesMap['wasRestarted'] ?? false;       final wasTransited = statesMap['wasTransited'] ?? false;       if (wasRestarted &amp;&amp; wasTransited) {         return const Center(           child: Padding(             padding: EdgeInsets.symmetric(horizontal: 16),             child: Column(               mainAxisAlignment: MainAxisAlignment.center,               children: [                 Text(                   '\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043e\u043f\u043b\u0430\u0442\u044b \u0437\u0430\u043a\u0430\u0437\u0430...',                   textAlign: TextAlign.center,                   style: TextStyle(fontSize: 21),                 ),                 SizedBox(height: 16),                 CircularProgressIndicator(),               ],             ),           ),         );       }       ...     } )   <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0431\u0430\u043d\u043a\u0430. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u0430\u043d\u043a\u043e\u0432 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0430\u0442\u0438\u0439. \u0418\u0437 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0431\u0435\u0440\u0435\u043c <code>schema<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432 \u0441\u0441\u044b\u043b\u043a\u0435 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 <code>https<\/code>.<\/p>\n<pre><code class=\"dart\">Future&lt;void> _openBank(BuildContext context, {required String schema}) async {   ScaffoldMessenger.of(context).removeCurrentSnackBar();   final paymentUrl = widget.paymentUrl.replaceAll(RegExp('https:\/\/'), '');   final link = '$schema:\/\/$paymentUrl';   try {     final wasLaunched = await launchUrlString(       link,       mode: LaunchMode.externalApplication,     );     if (!mounted) return;     _statesMapNotifier.value = {       'wasRestarted': false,       'wasTransited': wasLaunched,     };   } on Object {     if (!mounted) return;     ScaffoldMessenger.of(context).showSnackBar(       const SnackBar(content: Text('\u0422\u0430\u043a\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430 \u043d\u0435\u0442')),     );   } } <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0434\u0438\u043f\u043b\u0438\u043d\u043a \u043e\u0442\u043a\u0440\u044b\u043b\u0441\u044f (<code>wasLaunched<\/code> \u0441\u0442\u0430\u043b <code>true<\/code>), \u0444\u043b\u0430\u0433 <code>wasRestarted<\/code> \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code>false<\/code>. <code>wasRestarted<\/code> \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044e\u0442 \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e. \u0422\u0430\u043a \u043c\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u043e\u0442 \u0442\u0430\u043a.<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/64f\/e15\/278\/64fe152786af8a419fc776a2eb280c3c.gif\" width=\"1066\" height=\"600\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/64f\/e15\/278\/64fe152786af8a419fc776a2eb280c3c.gif\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"dart\">void main() => runApp(const MyApp());  class MyApp extends StatelessWidget {   const MyApp({super.key});    static const _paymentUrl =       'https:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G?type=01&amp;bank=100000000007&amp;crc=0C8A';    @override   Widget build(BuildContext context) {     return const MaterialApp(       title: 'SBP Pay Demo',       debugShowCheckedModeBanner: false,       home: SbpPayScreen(paymentUrl: _paymentUrl),     );   } }  class SbpPayScreen extends StatefulWidget {   const SbpPayScreen({     required this.paymentUrl,     super.key,   });    final String paymentUrl;    @override   State&lt;SbpPayScreen> createState() => _SbpPayScreenState(); }  class _SbpPayScreenState extends State&lt;SbpPayScreen>     with WidgetsBindingObserver {   late final ValueNotifier&lt;Map&lt;String, bool>> _statesMapNotifier;   late final AppLifecycleListener _lifecycleListener;    @override   void initState() {     super.initState();     WidgetsBinding.instance.addObserver(this);      _statesMapNotifier = ValueNotifier&lt;Map&lt;String, bool>>(       {'wasRestarted': false, 'wasTransited': false},     );      _lifecycleListener = AppLifecycleListener(       onRestart: () {         _statesMapNotifier.value = {           'wasRestarted': true,           'wasTransited': _statesMapNotifier.value['wasTransited'] ?? false,         };       },     );      _statesMapNotifier.addListener(() {       final wasRestarted = _statesMapNotifier.value['wasRestarted'] ?? false;       final wasTransited = _statesMapNotifier.value['wasTransited'] ?? false;       if (wasRestarted &amp;&amp; wasTransited) {         Future.delayed(           const Duration(seconds: 4),           () {             _statesMapNotifier.value = {               'wasRestarted': false,               'wasTransited': false,             };           },         );       }     });   }    @override   void dispose() {     _lifecycleListener.dispose();     _statesMapNotifier.dispose();     WidgetsBinding.instance.removeObserver(this);     super.dispose();   }    @override   Widget build(BuildContext context) {     return Scaffold(       appBar: AppBar(         title: ValueListenableBuilder(           valueListenable: _statesMapNotifier,           builder: (_, statesMap, __) {             final wasRestarted = statesMap['wasRestarted'] ?? false;             final wasTransited = statesMap['wasTransited'] ?? false;             return Column(               crossAxisAlignment: CrossAxisAlignment.start,               children: [                 Text(                   '\u0421\u0432\u0435\u0440\u043d\u0443\u0442\u043e \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u043e: $wasRestarted',                   style: const TextStyle(fontSize: 18),                 ),                 Text(                   '\u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 \u0431\u0430\u043d\u043a: $wasTransited',                   style: const TextStyle(fontSize: 18),                 ),               ],             );           },         ),       ),       body: SafeArea(         child: ValueListenableBuilder(           valueListenable: _statesMapNotifier,           builder: (_, statesMap, __) {             final wasRestarted = statesMap['wasRestarted'] ?? false;             final wasTransited = statesMap['wasTransited'] ?? false;             if (wasRestarted &amp;&amp; wasTransited) {               return const Center(                 child: Padding(                   padding: EdgeInsets.symmetric(horizontal: 16),                   child: Column(                     mainAxisAlignment: MainAxisAlignment.center,                     children: [                       Text(                         '\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043e\u043f\u043b\u0430\u0442\u044b \u0437\u0430\u043a\u0430\u0437\u0430...',                         textAlign: TextAlign.center,                         style: TextStyle(fontSize: 21),                       ),                       SizedBox(height: 16),                       CircularProgressIndicator(),                     ],                   ),                 ),               );             }              return FutureBuilder&lt;List&lt;BankItem>>(               future: _getBankList(),               builder: (context, snapshot) {                 if (snapshot.connectionState == ConnectionState.waiting) {                   return const Center(child: CircularProgressIndicator());                 }                  final data = snapshot.data ?? [];                  if (data.isEmpty) {                   return Center(                     child: Column(                       mainAxisSize: MainAxisSize.min,                       children: [                         const Text(                           '\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u0430\u043d\u043a\u043e\u0432',                           style: TextStyle(fontSize: 21),                         ),                         const SizedBox(height: 8),                         ElevatedButton(                           onPressed: _getBankList,                           child: const Text(                             '\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c',                             style: TextStyle(fontSize: 21),                           ),                         )                       ],                     ),                   );                 }                  return ListView.separated(                   itemCount: data.length,                   padding: const EdgeInsets.all(16),                   separatorBuilder: (_, __) => const SizedBox(height: 8),                   itemBuilder: (context, index) {                     final bank = data[index];                      return InkWell(                       onTap: () => _openBank(context, schema: bank.schema),                       child: Row(                         mainAxisAlignment: MainAxisAlignment.spaceBetween,                         children: [                           Flexible(                             child: Row(                               children: [                                 Image.network(                                   bank.logoURL,                                   height: 40,                                   width: 40,                                 ),                                 const SizedBox(width: 8),                                 Flexible(                                   child: Text(                                     bank.bankName,                                     maxLines: 1,                                     overflow: TextOverflow.ellipsis,                                   ),                                 ),                               ],                             ),                           ),                           const Icon(Icons.arrow_forward_ios_rounded, size: 16),                         ],                       ),                     );                   },                 );               },             );           },         ),       ),     );   }    Future&lt;void> _openBank(BuildContext context, {required String schema}) async {     ScaffoldMessenger.of(context).removeCurrentSnackBar();      final paymentUrl = widget.paymentUrl.replaceAll(RegExp('https:\/\/'), '');     final link = '$schema:\/\/$paymentUrl';      try {       final wasLaunched = await launchUrlString(         link,         mode: LaunchMode.externalApplication,       );        if (!mounted) return;       _statesMapNotifier.value = {         'wasRestarted': false,         'wasTransited': wasLaunched,       };     } on Object {       if (!mounted) return;       ScaffoldMessenger.of(context).showSnackBar(         const SnackBar(content: Text('\u0422\u0430\u043a\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430 \u043d\u0435\u0442')),       );     }   }    Future&lt;List&lt;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&lt;String, dynamic>;       final bankList = decodedMap['dictionary'] as List;       final mappedList = &lt;BankItem>[];        for (final item in bankList) {         final bankName = item['bankName'] as String?;         final logoURL = item['logoURL'] as String?;         final schema = item['schema'] as String?;          if (schema == null || logoURL == null || bankName == null) continue;         if (schema.isEmpty || logoURL.isEmpty || bankName.isEmpty) continue;          mappedList.add(           BankItem(             bankName: utf8.decode(bankName.codeUnits),             logoURL: logoURL,             schema: schema,           ),         );       }        return mappedList;     } on Object {       return &lt;BankItem>[];     }   } }  class BankItem {   const BankItem({     required this.bankName,     required this.logoURL,     required this.schema,   });    final String bankName;   final String logoURL;   final String schema;    @override   bool operator ==(Object other) =>       identical(this, other) ||       other is BankItem &amp;&amp;           runtimeType == other.runtimeType &amp;&amp;           bankName == other.bankName &amp;&amp;           logoURL == other.logoURL &amp;&amp;           schema == other.schema;    @override   int get hashCode => bankName.hashCode ^ logoURL.hashCode ^ schema.hashCode; } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u043a\u0430\u043a \u0432\u044b \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442\u0435 \u0421\u0411\u041f, \u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438 \u0438 \u043e\u043f\u044b\u0442\u043e\u043c.   <\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/824964\/\"> https:\/\/habr.com\/ru\/articles\/824964\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 <a href=\"https:\/\/habr.com\/ru\/users\/RonFall\/\"><u>\u041c\u0443\u0440\u0430\u0442 \u041d\u0430\u0441\u0438\u0440\u043e\u0432<\/u><\/a>, \u044f Flutter-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <a href=\"https:\/\/friflex.com\/flutter\/\"><u>\u0432 Friflex<\/u><\/a>. \u041c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0438 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 Flutter.\u00a0<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c, \u043a\u0430\u043a \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0421\u0411\u041f \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/sbp.nspk.ru\/file\/sbp_SDK.zip\"><u>\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u041d\u0421\u041f\u041a<\/u><\/a> (\u041d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u043b\u0430\u0442\u0435\u0436\u043d\u044b\u0445 \u043a\u0430\u0440\u0442). \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0432\u0443\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u0437 pub.dev.<\/p>\n<h3>\u0412\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443<\/h3>\n<p>\u041d\u0430 \u0441\u0430\u0439\u0442\u0435 API \u0421\u0411\u041f \u043e\u043f\u0438\u0441\u0430\u043d\u043e, \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u043b\u044f \u043e\u043f\u043b\u0430\u0442\u044b \u043f\u043e <a href=\"https:\/\/sbp.nspk.ru\/api\/#section\/Format-Funkcionalnoj-ssylki-SBP\"><u>\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0435<\/u><\/a>. \u041e\u0431\u044b\u0447\u043d\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u0434\u043b\u044f \u043e\u043f\u043b\u0430\u0442\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0430\u0439\u0442 \u0421\u0411\u041f, \u0433\u0434\u0435 \u0438\u0437 \u043d\u0435\u0435 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f QR-\u043a\u043e\u0434. \u0424\u043e\u0440\u043c\u0430\u0442 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0438:<\/p>\n<pre><code class=\"markdown\">https:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G? type=01&amp;bank=100000000007&amp;crc=0C8A<\/code><\/pre>\n<p>\u041d\u0435\u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e QR-\u043a\u043e\u0434\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0431\u0430\u043d\u043a\u0430. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043e\u043a\u043d\u043e \u043e\u043f\u043b\u0430\u0442\u044b \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u0440\u0438\u0435\u043c\u0449\u0438\u043a\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0430.\u00a0<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u0421\u0411\u041f, \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u044f \u0441\u0445\u0435\u043c\u0430. \u042d\u0442\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 id. \u041e\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u0432 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0421\u0411\u041f, \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/qr.nspk.ru\/proxyapp\/c2bmembers.json\"><u>\u0437\u0434\u0435\u0441\u044c.<\/u><\/a><\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043f\u043e\u043b\u0435 <code>schema<\/code> \u0438\u0437 \u0441\u0441\u044b\u043b\u043a\u0438 \u0432\u044b\u0448\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u0441\u0441\u044b\u043b\u043a\u0443 \u043e\u043f\u043b\u0430\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u043e <code>https<\/code>, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0434\u0438\u043f\u043b\u0438\u043d\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0441\u044b\u043b\u043a\u0430 \u0434\u043b\u044f \u0411\u0430\u043d\u043a\u0430 \u041e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"markdown\">bank100000000015:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G? type=01&amp;bank=100000000007&amp;crc=0C8A<\/code><\/pre>\n<p>\u0411\u0430\u043d\u043a\u0438, \u0447\u044c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u044b\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0438\u0437 App Store \u0438 Google Play, \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u043e\u043d\u044b. \u0423 \u043a\u043b\u043e\u043d\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f <code>package_name<\/code>, \u0430 \u043f\u043e\u043b\u0435 <code>schema<\/code> \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0443 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u0435\u0441\u043b\u0438 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0421\u0431\u0435\u0440\u0411\u0430\u043d\u043a \u0441\u0442\u043e\u0438\u0442 \u0430\u043d\u0430\u043b\u043e\u0433, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0423\u043c\u043d\u044b\u0439 \u041e\u043d\u043b\u0430\u0439\u043d, \u043e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e \u0421\u0411\u041f \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e.<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0414\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0448\u0430\u0433\u043e\u0432:<\/p>\n<p>1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <a href=\"https:\/\/qr.nspk.ru\/proxyapp\/c2bmembers.json\"><u>\u0441\u043f\u0438\u0441\u043e\u043a<\/u><\/a> \u0431\u0430\u043d\u043a\u043e\u0432.<\/p>\n<p>2. \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0442\u0440\u0435\u0445 \u043f\u043e\u043b\u0435\u0439: <code>bankName<\/code>, <code>logoURL<\/code> \u0438 <code>schema<\/code>.<\/p>\n<p>3. \u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u044d\u0442\u0438 \u043f\u043e\u043b\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432\u0441\u0435 \u0431\u0430\u043d\u043a\u0438 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435.<\/p>\n<p>4. \u0421\u0442\u0430\u0432\u0438\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0430\u0442\u0438\u044f (<code>GestureDetector<\/code>) \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0431\u0430\u043d\u043a. \u041c\u0435\u043d\u044f\u0435\u043c \u0432 \u0441\u0441\u044b\u043b\u043a\u0430\u0445 \u043e\u043f\u043b\u0430\u0442\u044b <code>https<\/code> \u043d\u0430 <code>schema<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0431\u0430\u043d\u043a\u0430.<\/p>\n<p>5. \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u043c \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>AppLifecycleListener<\/code>. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043a\u043e\u043b\u043b\u0431\u0435\u043a <code>onRestart<\/code>, \u0432 \u043d\u0435\u043c \u0441\u0442\u0430\u0432\u0438\u043c \u0444\u043b\u0430\u0433. \u0412\u0442\u043e\u0440\u043e\u0439 \u0444\u043b\u0430\u0433 \u0441\u0442\u0430\u0432\u0438\u043c \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430.<\/p>\n<p>6. \u041f\u043e\u0441\u043b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043a\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u043e\u043f\u043b\u0430\u0442\u044b.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0430 \u043f\u0430\u043a\u0435\u0442\u0430. \u0412 <code>pubspec.yaml<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c:<\/p>\n<pre><code class=\"yaml\">dependencies:   http: any   url_launcher: any<\/code><\/pre>\n<p>\u0412\u0435\u0440\u0441\u0438\u044e \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0430\u0448\u0435\u0433\u043e Flutter SDK. \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u0430\u043d\u043a\u043e\u0432. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439: <code>bankName<\/code>, <code>logoURL<\/code> \u0438 <code>schema<\/code>.<\/p>\n<pre><code class=\"dart\">class BankItem {   const BankItem({     required this.bankName,     required this.logoURL,     required this.schema,   });    final String bankName;   final String logoURL;   final String schema;    @override   bool operator ==(Object other) =>       identical(this, other) ||       other is BankItem &amp;&amp;           runtimeType == other.runtimeType &amp;&amp;           bankName == other.bankName &amp;&amp;           logoURL == other.logoURL &amp;&amp;           schema == other.schema;    @override   int get hashCode => bankName.hashCode ^ logoURL.hashCode ^ schema.hashCode; } <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432-\u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0421\u0411\u041f:  <\/p>\n<pre><code class=\"dart\">Future&lt;List&lt;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&lt;String, dynamic>;     final bankList = decodedMap['dictionary'] as List;     final mappedList = &lt;BankItem>[];      for (final item in bankList) {       final bankName = item['bankName'] as String?;       final logoURL = item['logoURL'] as String?;       final schema = item['schema'] as String?;       if (schema == null || logoURL == null || bankName == null) continue;       if (schema.isEmpty || logoURL.isEmpty || bankName.isEmpty) continue;       mappedList.add(         BankItem(           bankName: utf8.decode(bankName.codeUnits),           logoURL: logoURL,           schema: schema,         ),       );     }      return mappedList;   } on Object {     return &lt;BankItem>[];   } } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>utf8<\/code> \u0438\u0437-\u0437\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u043e\u0439. \u0415\u0441\u043b\u0438 \u0443 \u0431\u0430\u043d\u043a\u0430 \u043d\u0435\u0442 \u0441\u0445\u0435\u043c\u044b, \u0438\u043c\u0435\u043d\u0438 \u0438\u043b\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u043d\u043a\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>FutureBuilder<\/code> \u0438 <code>ListView<\/code>.<\/p>\n<p>\u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u043e \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0438\u0437 \u043d\u0435\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>WidgetsBindingObserver<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>AppLifecycleListener.<\/code>\u00a0<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c <code>Map<\/code> \u0441 \u0434\u0432\u0443\u043c\u044f \u0444\u043b\u0430\u0433\u0430\u043c\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0434\u0432\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f: \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u043f\u0440\u0438 <code>onRestart<\/code>) \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430 \u043f\u043e \u0434\u0438\u043f\u043b\u0438\u043d\u043a\u0443. <code>Map<\/code> \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432 <code>ValueNotifier<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043e\u0431\u0430 \u0444\u043b\u0430\u0433\u0430 <code>true<\/code>.<\/p>\n<pre><code class=\"dart\">class _SbpPayScreenState extends State&lt;SbpPayScreen>     with WidgetsBindingObserver {   late final ValueNotifier&lt;Map&lt;String, bool>> _statesMapNotifier;   late final AppLifecycleListener _lifecycleListener;    @override   void initState() {     super.initState();     WidgetsBinding.instance.addObserver(this);     _statesMapNotifier = ValueNotifier&lt;Map&lt;String, bool>>(       {'wasRestarted': false, 'wasTransited': false},     );     _lifecycleListener = AppLifecycleListener(       onRestart: () {         _statesMapNotifier.value = {           'wasRestarted': true,           'wasTransited': _statesMapNotifier.value['wasTransited'] ??               false,         };       },     );     _statesMapNotifier.addListener(() {       final wasRestarted = _statesMapNotifier.value['wasRestarted'] ??           false;       final wasTransited = _statesMapNotifier.value['wasTransited'] ??           false;       if (wasRestarted &amp;&amp; wasTransited) {         Future.delayed(           const Duration(seconds: 4),               () {             _statesMapNotifier.value = {               'wasRestarted': false,               'wasTransited': false,             };           },         );       }     });   }    @override   void dispose() {     _lifecycleListener.dispose();     _statesMapNotifier.dispose();     WidgetsBinding.instance.removeObserver(this);     super.dispose();   }    ... } <\/code><\/pre>\n<p>\u0412 <code>Map<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0432\u0430 \u043a\u043b\u044e\u0447\u0430:\u00a0<\/p>\n<ul>\n<li>\n<p><code>wasRestarted<\/code> \u2014 \u0444\u043b\u0430\u0433, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0441\u0432\u0435\u0440\u043d\u0443\u0442\u043e \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u043e \u0441\u043d\u043e\u0432\u0430;<\/p>\n<\/li>\n<li>\n<p><code>wasTransited<\/code> \u2014 \u0444\u043b\u0430\u0433, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u043b \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430.\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0431\u0430 \u0444\u043b\u0430\u0433\u0430 \u0441\u0442\u0430\u043b\u0438 <code>true<\/code>, \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430. \u0427\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c, \u043e\u043f\u043b\u0430\u0442\u0438\u043b \u043b\u0438 \u043e\u043d, \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0443 \u0431\u044d\u043a\u0435\u043d\u0434\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443 <code>_statesMapNotifier<\/code> \u0441\u0442\u043e\u0438\u0442 \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u0441 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0435\u0439 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u044f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u0412 <code>build<\/code> \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <code>ValueListenableBuilder<\/code>. \u041e\u043d \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<\/p>\n<pre><code class=\"dart\">ValueListenableBuilder(     valueListenable: _statesMapNotifier,     builder: (_, statesMap, __) {       final wasRestarted = statesMap['wasRestarted'] ?? false;       final wasTransited = statesMap['wasTransited'] ?? false;       if (wasRestarted &amp;&amp; wasTransited) {         return const Center(           child: Padding(             padding: EdgeInsets.symmetric(horizontal: 16),             child: Column(               mainAxisAlignment: MainAxisAlignment.center,               children: [                 Text(                   '\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043e\u043f\u043b\u0430\u0442\u044b \u0437\u0430\u043a\u0430\u0437\u0430...',                   textAlign: TextAlign.center,                   style: TextStyle(fontSize: 21),                 ),                 SizedBox(height: 16),                 CircularProgressIndicator(),               ],             ),           ),         );       }       ...     } )   <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0431\u0430\u043d\u043a\u0430. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u0430\u043d\u043a\u043e\u0432 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0430\u0442\u0438\u0439. \u0418\u0437 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0431\u0435\u0440\u0435\u043c <code>schema<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432 \u0441\u0441\u044b\u043b\u043a\u0435 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 <code>https<\/code>.<\/p>\n<pre><code class=\"dart\">Future&lt;void> _openBank(BuildContext context, {required String schema}) async {   ScaffoldMessenger.of(context).removeCurrentSnackBar();   final paymentUrl = widget.paymentUrl.replaceAll(RegExp('https:\/\/'), '');   final link = '$schema:\/\/$paymentUrl';   try {     final wasLaunched = await launchUrlString(       link,       mode: LaunchMode.externalApplication,     );     if (!mounted) return;     _statesMapNotifier.value = {       'wasRestarted': false,       'wasTransited': wasLaunched,     };   } on Object {     if (!mounted) return;     ScaffoldMessenger.of(context).showSnackBar(       const SnackBar(content: Text('\u0422\u0430\u043a\u043e\u0433\u043e \u0431\u0430\u043d\u043a\u0430 \u043d\u0435\u0442')),     );   } } <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0434\u0438\u043f\u043b\u0438\u043d\u043a \u043e\u0442\u043a\u0440\u044b\u043b\u0441\u044f (<code>wasLaunched<\/code> \u0441\u0442\u0430\u043b <code>true<\/code>), \u0444\u043b\u0430\u0433 <code>wasRestarted<\/code> \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code>false<\/code>. <code>wasRestarted<\/code> \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044e\u0442 \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e. \u0422\u0430\u043a \u043c\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043a\u0430 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u043e\u0442 \u0442\u0430\u043a.<\/p>\n<figure class=\"bordered full-width\"><\/figure>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"dart\">void main() => runApp(const MyApp());  class MyApp extends StatelessWidget {   const MyApp({super.key});    static const _paymentUrl =       'https:\/\/qr.nspk.ru\/AS100001ORTF4GAF80KPJ53K186D9A3G?type=01&amp;bank=100000000007&amp;crc=0C8A';    @override   Widget build(BuildContext context) {     return const MaterialApp(       title: 'SBP Pay Demo',       debugShowCheckedModeBanner: false,       home: SbpPayScreen(paymentUrl: _paymentUrl),     );   } }  class SbpPayScreen extends StatefulWidget {   const SbpPayScreen({     required this.paymentUrl,     super.key,   });    final String paymentUrl;    @override   State&lt;SbpPayScreen> createState() => _SbpPayScreenState(); }  class _SbpPayScreenState extends State&lt;SbpPayScreen>     with WidgetsBindingObserver {   late final ValueNotifier&lt;Map&lt;String, bool>> _statesMapNotifier;   late final AppLifecycleListener _lifecycleListener;    @override   void initState() {     super.initState();     WidgetsBinding.instance.addObserver(this);      _statesMapNotifier = ValueNotifier&lt;Map&lt;String, bool>>(       {'wasRestarted': false, 'wasTransited': false},     );      _lifecycleListener = AppLifecycleListener(       onRestart: () {         _statesMapNotifier.value = {           'wasRestarted': true,<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-380134","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/380134","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=380134"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/380134\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=380134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=380134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=380134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}