{"id":386529,"date":"2024-06-29T06:45:39","date_gmt":"2024-06-29T06:45:39","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=386529"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=386529","title":{"rendered":"<span>Flutter + Socket.io \u2014 \u041e\u0431\u043c\u0435\u043d \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/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<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438\u043c\u0435\u0435\u0442 \u0440\u0435\u0448\u0430\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e, \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u044d\u0442\u043e\u0433\u043e, \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0422\u043e\u0433\u0434\u0430 \u0438 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f <strong>WebSocket<\/strong>.<\/p>\n<blockquote>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0435\u0431\u0435 \u0442\u0440\u0443\u0431\u0443, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0434\u0430 (<em>\u0434\u0430\u043d\u043d\u044b\u0435<\/em>) \u043c\u043e\u0436\u0435\u0442 \u0442\u0435\u0447\u044c \u0432 \u043e\u0431\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u044b.<\/p>\n<\/blockquote>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/529\/45f\/239\/52945f239ceb29f80282b886207c01b1.png\" width=\"1400\" height=\"1025\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/529\/45f\/239\/52945f239ceb29f80282b886207c01b1.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 WebSocket \u043d\u0430\u0434 HTTPS<\/h3>\n<p>WebSocket \u0438\u043c\u0435\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432 \u043d\u0430\u0434 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c HTTPS, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, <strong>WebSocket<\/strong> \u043e\u0431\u044b\u0447\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0432 \u043e\u0431\u043c\u0435\u043d\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0438 \u043e\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430 <em>\u0437\u0430\u043f\u0440\u043e\u0441-\u043e\u0442\u0432\u0435\u0442<\/em>, \u043a\u0430\u043a \u0432 HTTP, WebSocket \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043e\u0431\u043c\u0435\u043d \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441 \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432.<\/p>\n<blockquote>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u0430\u043a\u043e\u043c\u0443 \u043c\u0435\u0442\u043e\u0434\u0443 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.<\/p>\n<\/blockquote>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Node \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/h3>\n<p>\u041d\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0434 \u0434\u0432\u0443\u043c\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438: \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u043d\u0430 <strong>Node.js<\/strong> \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, <strong>Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c<\/strong>. \u0412 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u044d\u0445\u043e-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u0441\u043b\u0443\u0448\u0430\u0435\u0442 (\u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438 &#8212; \u0442\u0435\u043c, \u043a\u0442\u043e <em>\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/em>). \u0423\u0441\u043b\u043e\u0432\u043d\u043e<em>, \u0441\u0435\u0440\u0432\u0435\u0440<\/em> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043c\u043e\u0441\u0442\u043e\u043c \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e73\/e58\/f6a\/e73e58f6a40f4f21a909e83bf9f8d14a.png\" width=\"1400\" height=\"864\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e73\/e58\/f6a\/e73e58f6a40f4f21a909e83bf9f8d14a.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u0440\u043e\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c WebSocket \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438. \u0427\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>socket_chat_server<\/code><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b <code>package.json<\/code> \u0432 \u044d\u0442\u043e\u0439 \u043f\u0430\u043f\u043a\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u043f\u0430\u043f\u043a\u0443, \u0435\u0441\u043b\u0438 \u0435\u0449\u0435 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u044d\u0442\u043e<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npm init<\/code> \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0444\u0430\u0439\u043b <code>package.json<\/code>, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0434\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<h4>package.json<\/h4>\n<pre><code class=\"json\">{   \"name\": \"socket_chat_server\",   \"version\": \"1.0.0\",   \"description\": \"\",   \"main\": \"index.js\",   \"scripts\": {     \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\",     \"start\": \"node .\/index.js\"   },   \"author\": \"\",   \"license\": \"ISC\",   \"dependencies\": {     \"http\": \"0.0.1-security\",     \"socket.io\": \"^2.3.0\"   },   \"engines\": {     \"node\": \"10.x\"   } }<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c <strong>npm<\/strong> \u043c\u043e\u0434\u0443\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm install<\/code>.<\/p>\n<h4>index.js<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<em>,<\/em> \u043a\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435:<\/p>\n<pre><code class=\"javascript\">const server = require('http').createServer() const io = require('socket.io')(server)  io.on('connection', function (client) {    console.log('client connect...', client.id);    client.on('typing', function name(data) {     console.log(data);     io.emit('typing', data)   })    client.on('message', function name(data) {     console.log(data);     io.emit('message', data)   })    client.on('location', function name(data) {     console.log(data);     io.emit('location', data);   })    client.on('connect', function () {   })    client.on('disconnect', function () {     console.log('client disconnect...', client.id)     \/\/ handleDisconnect()   })    client.on('error', function (err) {     console.log('received error from client:', client.id)     console.log(err)   }) })  var server_port = process.env.PORT || 3000; server.listen(server_port, function (err) {   if (err) throw err   console.log('Listening on port %d', server_port); });<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u0437\u0430\u0442\u0435\u043c \u0438\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 <code>client.on<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f, \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443 &#171;\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435&#187;, \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430\u0437\u0430\u0434 \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. \u0412\u0430\u0448 WebSocket \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u043d\u0430 <code>http:\/\/localhost:3000<\/code>.<\/p>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm start<\/code> \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440. \u0412\u044b\u0432\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0438\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7d6\/a2d\/a7a\/7d6a2da7afd3a90509552c8d9ebed309.png\" width=\"1400\" height=\"863\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7d6\/a2d\/a7a\/7d6a2da7afd3a90509552c8d9ebed309.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Flutter<\/h3>\n<p><strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a\u0430\u043a \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u043a \u043d\u0435\u043c\u0443. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>flutter create socket_chat<\/code>. \u041d\u0430 <a href=\"https:\/\/pub.dev\">pub.dev<\/a> \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e <strong>socket.io<\/strong> \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u043d\u043e, \u043a\u0430\u043a \u043c\u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, <a href=\"https:\/\/pub.dev\/packages\/socket_io_client\">socket_io_client<\/a> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0449\u0435 \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u043f\u0438\u0435\u0439 <em>js<\/em> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d74\/6ea\/e0e\/d746eae0e707ec90cf0e6b16e11116a9.png\" width=\"1400\" height=\"1074\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d74\/6ea\/e0e\/d746eae0e707ec90cf0e6b16e11116a9.png\"\/><figcaption><\/figcaption><\/figure>\n<h4>\u200b \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/h4>\n<p>\u041f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a <code>localhost<\/code> \u043d\u0430 <strong>Android<\/strong> \u044d\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 <code>adb reverse tcp:3000 tcp:3000<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a <code>localhost<\/code> \u0447\u0435\u0440\u0435\u0437 <code>http:\/\/127.0.0.1:3000<\/code> \u043d\u0430 Android\u00a0(\u0430 \u0431\u0435\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <a href=\"https:\/\/developer.android.com\/studio\/run\/emulator-networking\"><u>\u043c\u043e\u0436\u043d\u043e<\/u><\/a> \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a <code>localhost<\/code> \u0441 Android \u044d\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/10.0.2.2:3000<\/code>. \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430)  <em>. <\/em>\u041f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e <em>\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f-\u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430<\/em> \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e <code>home.dart<\/code>.<\/p>\n<h4>home.dart<\/h4>\n<pre><code class=\"dart\">...     Socket socket;      @override   void initState() {     super.initState();     connectToServer();   }    void connectToServer() {     try {             \/\/ Configure socket transports must be sepecified       socket = io('http:\/\/127.0.0.1:3000', &lt;String, dynamic>{         'transports': ['websocket'],         'autoConnect': false,       });             \/\/ Connect to websocket       socket.connect();             \/\/ Handle socket events       socket.on('connect', (_) => print('connect: ${socket.id}'));       socket.on('location', handleLocationListen);       socket.on('typing', handleTyping);       socket.on('message', handleMessage);       socket.on('disconnect', (_) => print('disconnect'));       socket.on('fromServer', (_) => print(_));      } catch (e) {       print(e.toString());     }        }       \/\/ Send Location to Server   sendLocation(Map&lt;String, dynamic> data) {     socket.emit(\"location\", data);   }       \/\/ Listen to Location updates of connected usersfrom server   handleLocationListen(Map&lt;String, dynamic> data) async {     print(data);   }    \/\/ Send update of user's typing status    sendTyping(bool typing) {     socket.emit(\"typing\",       {         \"id\": socket.id,          \"typing\": typing,       });   }       \/\/ Listen to update of typing status from connected users   void handleTyping(Map&lt;String, dynamic> data) {     print(data);   }    \/\/ Send a Message to the server   sendMessage(String message) {       socket.emit(\"message\",         {           \"id\": socket.id,           \"message\": message, \/\/ Message to be sent           \"timestamp\": DateTime.now().millisecondsSinceEpoch,         },       );   }    \/\/ Listen to all message events from connected users   void handleMessage(Map&lt;String, dynamic> data) {     print(data);   }   ...<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0448\u0435 &#8212; \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f WebSocket, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CI\/CD \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h3>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 WebSocket \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f(Test, Dev, \u0438\u043b\u0438 Prod). \u0425\u043e\u0440\u043e\u0448\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u043e\u0439 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0438\u0435 \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0443\u0447\u0430\u0441\u0442\u043a\u043e\u0432 \u043a\u043e\u0434\u0430(\u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, <em>URL<\/em>-\u0430\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430) \u0432 \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e <strong>CI\/CD<\/strong> <strong>\u0441\u0431\u043e\u0440\u043a\u0443<\/strong>.<\/p>\n<blockquote>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>CI\/CD<\/strong> \u0441\u0442\u0430\u043b\u043e \u043d\u043e\u0440\u043c\u043e\u0439 \u0434\u043b\u044f \u0445\u043e\u0440\u043e\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0445 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u044b\u0445 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043e\u043d\u043e\u0433\u043e \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 <strong>Flutter<\/strong> \u043f\u0430\u043a\u0442\u043e\u0432, <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e.<\/p>\n<\/blockquote>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u0440\u0435\u0434\u044b <strong>CI\/CD<\/strong> \u0432 Codemagic. \u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0439\u0442\u0438 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c, \u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c \u0438 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0440\u0435\u0434\u044b <code>dev<\/code> \u0438 <code>prod<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e <code>.yaml<\/code> \u0444\u0430\u0439\u043b\u0430 \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0441\u0432\u043e\u0439 <a href=\"https:\/\/codemagic.io\/start\/\">Codemagic.io<\/a> \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u043a\u0440\u0430\u0442\u0447\u0430\u0439\u0448\u0438\u0435 \u0441\u0440\u043e\u043a\u0438.<\/p>\n<p>\u0414\u0430, \u044f \u0437\u043d\u0430\u044e, \u0447\u0442\u043e \u0435\u0441\u0442\u044c UI, \u043d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0442\u043e\u043a CI\/CD, \u043d\u0435 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435, \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0441 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c. \u0418\u0442\u0430\u043a, \u0432\u043e\u0442 \u0431\u044b\u0441\u0442\u0440\u044b\u0439, \u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e:<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u0421\u0440\u0435\u0434\u044b<\/h4>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <strong>CI\/CD<\/strong> \u0438 \u0441\u0440\u0435\u0434\u044b \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 Codemagic, \u0442\u0430\u043a \u0438 \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u0435\u0434\u044b \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1a6\/6d5\/bb0\/1a66d5bb0668300d6ee61f4efd36b7be.png\" width=\"394\" height=\"588\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a6\/6d5\/bb0\/1a66d5bb0668300d6ee61f4efd36b7be.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0441\u0442\u0430, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043d\u0430\u0448 \u043d\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 <strong>JSON<\/strong> \u0444\u0430\u0439\u043b <code>config\/appconfig.json<\/code>, \u0432 \u043d\u0435\u043c \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u044b \u043d\u0430\u0448\u0438 <em>URL<\/em>-\u0430\u0434\u0440\u0435\u0441\u0430 \u0445\u043e\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438. \u0412\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c.<\/p>\n<pre><code class=\"yaml\">assets:   - config\/app_config.json<\/code><\/pre>\n<p>\u041a\u043e\u0434 \u0432\u00a0<code>lib\/config\/config_loader.dart<\/code>\u00a0\u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e <strong>JSON<\/strong> \u0444\u0430\u0439\u043b\u0430\u00a0<code>(config\/appconfig.json)<\/code>. <\/p>\n<p><code>env.dart<\/code>\u00a0\u0438\u00a0<code>env_config.dart<\/code>\u00a0\u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0438\u0445 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0440\u0435\u0434 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432<code>_dev.dart<\/code>\u00a0\u0438\u00a0<code>_prod.dart<\/code>\u00a0.  <\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439\u00a0<code>main.dart<\/code>\u00a0\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439. \u041d\u043e \u0434\u043b\u044f \u043d\u0430\u0441, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0443\u043c\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430, \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b\u00a0<code>main_common.dart<\/code>\u00a0,\u00a0<code>main_dev.dart<\/code>\u00a0\u0438\u00a0<code>main_prod.dart<\/code>. \u041a\u0430\u043a \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f,\u00a0<code>main_common.dart<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u043c\u043e\u0441\u0442\u043e\u043c, \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u043c \u0441 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0439 \u0441\u0440\u0435\u0434\u043e\u0439. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c\u00a0<code>flutter run -t lib\/main_dev.dart<\/code>\u00a0\u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u00a0<code>flutter run -t lib\/main_prod.dart<\/code>\u00a0\u0434\u043b\u044f \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<h4>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <strong>JSON<\/strong> \u043a\u043e\u043d\u0444\u0438\u0433 \u0432 <em>base64<\/em>, \u0447\u0442\u043e\u0431\u044b \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b \u0432 <code>codemagic.yaml<\/code>, <a href=\"https:\/\/docs.codemagic.io\/getting-started\/yaml\/\">\u0437\u0434\u0435\u0441\u044c <\/a>\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u044d\u0442\u043e\u043c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/155\/060\/6b9\/1550606b9d40b5a722f589c0017f9b35.png\" width=\"1400\" height=\"631\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/155\/060\/6b9\/1550606b9d40b5a722f589c0017f9b35.png\"\/><figcaption><\/figcaption><\/figure>\n<h4>Codemagic.yaml<\/h4>\n<p>Codemagic \u0434\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u043e\u043c <code>yaml<\/code> <a href=\"https:\/\/docs.codemagic.io\/getting-started\/yaml\/\">\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0435<\/a>. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043d\u0430\u0448 \u0444\u0430\u0439\u043b <code>codemagic.yaml<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"yaml\"># Automatically generated on 2020-11-16 UTC from https:\/\/codemagic.io\/app\/5fb2a25c605096f91720b983\/settings # Note that this configuration is not an exact match to UI settings. Review and adjust as necessary.  workflows:   default-workflow:     name: Default Workflow     max_build_duration: 60     environment:       vars:         APP_CONFIG: ewogICJnQXBpS2V5IjogIkFJemFTeUEzbl95bTlWUUU2NURyRUVpdDZobnNtWDgyR3FGb3Q0QSIKfQo=       flutter: stable       xcode: latest       cocoapods: default     scripts:       - |         # set up debug keystore         rm -f ~\/.android\/debug.keystore         keytool -genkeypair \\           -alias androiddebugkey \\           -keypass android \\           -keystore ~\/.android\/debug.keystore \\           -storepass android \\           -dname 'CN=Android Debug,O=Android,C=US' \\           -keyalg 'RSA' \\           -keysize 2048 \\           -validity 10000               - |         # set up local properties         echo \"flutter.sdk=$HOME\/programs\/flutter\" > \"$FCI_BUILD_DIR\/android\/local.properties\"               - cd . &amp;&amp; flutter packages pub get       - |         # Create directory if it doesn't exist         mkdir -p $FCI_BUILD_DIR\/config         # Write out the environment variable as a json file         echo $APP_CONFIG | base64 --decode > $FCI_BUILD_DIR\/config\/app_config.json               - cd . &amp;&amp; flutter build apk --debug -t lib\/main_dev.dart       - find . -name \"Podfile\" -execdir pod install \\;       - cd . &amp;&amp; flutter build ios --debug --no-codesign lib\/main_dev.dart     artifacts:       - build\/**\/outputs\/**\/*.apk       - build\/**\/outputs\/**\/*.aab       - build\/**\/outputs\/**\/mapping.txt       - build\/ios\/ipa\/*.ipa       - \/tmp\/xcodebuild_logs\/*.log       - flutter_drive.log     publishing:       email:         recipients:           - ogbondachiziaruhoma@gmail.com<\/code><\/pre>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0432 \u043d\u0430\u0448\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"yaml\">vars:   APP_CONFIG: ewogICJkZXZIb3N0IjogIkRldmVsb3BtZW50IEhvc3QiLAogICJwcm9kSG9zdCI6ICJQcm9kdWN0aW9uIEhvc3QiCn0K<\/code><\/pre>\n<p>\u0412\u0441\u0442\u0430\u0432\u0438\u0432 \u044d\u0442\u043e\u0442 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043a\u043e\u0434\u0430 \u0432\u044b\u0448\u0435, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u0430\u0439\u043b <code>\/config\/app_config.json<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 <strong>CI<\/strong> \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<pre><code># Write out the environment variable as a json file         echo $APP_CONFIG | base64 --decode > $FCI_BUILD_DIR\/config\/app_config.json<\/code><\/pre>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/strong> \u041d\u0430\u0448 \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0444\u0430\u0439\u043b <code>lib\/main_dev.dart<\/code> \u0432 \u0444\u0430\u0439\u043b\u0435 <code>codemagic.yaml<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d \u043d\u0430 <code>lib\/main_prod.dart<\/code>\u0434\u043b\u044f \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u0441\u0431\u043e\u0440\u043a\u0438<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f <code>codemagic.yaml<\/code> \u0444\u0430\u0439\u043b\u0430 \u043d\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0432\u0435\u0440\u0441\u0438\u0439(GitHub, bitbucket \u0438 \u0442.\u0434), \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 <strong>\u201cStart your first build\u201d<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d97\/f01\/ee1\/d97f01ee157c5e3bf55dcfb3e35e9cca.png\" width=\"532\" height=\"246\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d97\/f01\/ee1\/d97f01ee157c5e3bf55dcfb3e35e9cca.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0424\u0430\u0439\u043b \u0441\u0440\u0435\u0434\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7a2\/d20\/9a3\/7a2d209a3ae515599d6f68af1f3b2e83.png\" width=\"598\" height=\"380\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7a2\/d20\/9a3\/7a2d209a3ae515599d6f68af1f3b2e83.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430\u00a0<strong>\u201cStart new build\u201d<\/strong>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/391\/869\/4cb\/3918694cb0926c944d78848794350942.png\" width=\"1279\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/391\/869\/4cb\/3918694cb0926c944d78848794350942.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412\u0443\u0430\u043b\u044f, \u0441\u0431\u043e\u0440\u043a\u0430 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u0430.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d72\/85a\/2cc\/d7285a2cc7a9df88fd7c9482d08e5e3c.png\" width=\"1400\" height=\"999\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d72\/85a\/2cc\/d7285a2cc7a9df88fd7c9482d08e5e3c.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ccb\/fe8\/c89\/ccbfe8c897f39e00a0219b0cbe06cb98.png\" width=\"928\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ccb\/fe8\/c89\/ccbfe8c897f39e00a0219b0cbe06cb98.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>The end<\/h3>\n<p>\u0412\u0435\u0431-\u0441\u043e\u043a\u0435\u0442\u044b \u0431\u044b\u0441\u0442\u0440\u044b\u0435, \u043f\u0440\u043e\u0441\u0442\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438 \u0432\u0441\u0435 \u0435\u0449\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c <strong>&#171;RESTful&#187;<\/strong>. \u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0418 \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b\/\u0441\u043a\u0440\u044b\u0442\u044c \u0447\u0430\u0441\u0442\u0438 \u043a\u043e\u0434\u0430 \u0432 Codemagic CI\/CD, \u0438 \u0440\u0430\u0434\u043e\u0441\u0442\u044c \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437, \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0439 \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0432\u0435\u0431-\u0441\u043e\u043a\u0435\u0442\u043e\u0432 \u0438 Codemagic CI\/CD, \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0438, \u0447\u0442\u043e\u0431\u044b <a href=\"https:\/\/github.com\/Zfinix\/socket_chat\/\"><strong>\u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434<\/strong><\/a>.<\/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\/568356\/\"> https:\/\/habr.com\/ru\/articles\/568356\/<\/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<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438\u043c\u0435\u0435\u0442 \u0440\u0435\u0448\u0430\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e, \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u044d\u0442\u043e\u0433\u043e, \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0422\u043e\u0433\u0434\u0430 \u0438 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f <strong>WebSocket<\/strong>.<\/p>\n<blockquote>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0435\u0431\u0435 \u0442\u0440\u0443\u0431\u0443, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0434\u0430 (<em>\u0434\u0430\u043d\u043d\u044b\u0435<\/em>) \u043c\u043e\u0436\u0435\u0442 \u0442\u0435\u0447\u044c \u0432 \u043e\u0431\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u044b.<\/p>\n<\/blockquote>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 WebSocket \u043d\u0430\u0434 HTTPS<\/h3>\n<p>WebSocket \u0438\u043c\u0435\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432 \u043d\u0430\u0434 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c HTTPS, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, <strong>WebSocket<\/strong> \u043e\u0431\u044b\u0447\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0432 \u043e\u0431\u043c\u0435\u043d\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0438 \u043e\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430 <em>\u0437\u0430\u043f\u0440\u043e\u0441-\u043e\u0442\u0432\u0435\u0442<\/em>, \u043a\u0430\u043a \u0432 HTTP, WebSocket \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043e\u0431\u043c\u0435\u043d \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441 \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432.<\/p>\n<blockquote>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u0430\u043a\u043e\u043c\u0443 \u043c\u0435\u0442\u043e\u0434\u0443 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.<\/p>\n<\/blockquote>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Node \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/h3>\n<p>\u041d\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0434 \u0434\u0432\u0443\u043c\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438: \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u043d\u0430 <strong>Node.js<\/strong> \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, <strong>Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c<\/strong>. \u0412 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u044d\u0445\u043e-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u0441\u043b\u0443\u0448\u0430\u0435\u0442 (\u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438 &#8212; \u0442\u0435\u043c, \u043a\u0442\u043e <em>\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/em>). \u0423\u0441\u043b\u043e\u0432\u043d\u043e<em>, \u0441\u0435\u0440\u0432\u0435\u0440<\/em> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043c\u043e\u0441\u0442\u043e\u043c \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u0440\u043e\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c WebSocket \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438. \u0427\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>socket_chat_server<\/code><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b <code>package.json<\/code> \u0432 \u044d\u0442\u043e\u0439 \u043f\u0430\u043f\u043a\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u043f\u0430\u043f\u043a\u0443, \u0435\u0441\u043b\u0438 \u0435\u0449\u0435 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u044d\u0442\u043e<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npm init<\/code> \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0444\u0430\u0439\u043b <code>package.json<\/code>, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0434\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<h4>package.json<\/h4>\n<pre><code class=\"json\">{   \"name\": \"socket_chat_server\",   \"version\": \"1.0.0\",   \"description\": \"\",   \"main\": \"index.js\",   \"scripts\": {     \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\",     \"start\": \"node .\/index.js\"   },   \"author\": \"\",   \"license\": \"ISC\",   \"dependencies\": {     \"http\": \"0.0.1-security\",     \"socket.io\": \"^2.3.0\"   },   \"engines\": {     \"node\": \"10.x\"   } }<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c <strong>npm<\/strong> \u043c\u043e\u0434\u0443\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm install<\/code>.<\/p>\n<h4>index.js<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<em>,<\/em> \u043a\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435:<\/p>\n<pre><code class=\"javascript\">const server = require('http').createServer() const io = require('socket.io')(server)  io.on('connection', function (client) {    console.log('client connect...', client.id);    client.on('typing', function name(data) {     console.log(data);     io.emit('typing', data)   })    client.on('message', function name(data) {     console.log(data);     io.emit('message', data)   })    client.on('location', function name(data) {     console.log(data);     io.emit('location', data);   })    client.on('connect', function () {   })    client.on('disconnect', function () {     console.log('client disconnect...', client.id)     \/\/ handleDisconnect()   })    client.on('error', function (err) {     console.log('received error from client:', client.id)     console.log(err)   }) })  var server_port = process.env.PORT || 3000; server.listen(server_port, function (err) {   if (err) throw err   console.log('Listening on port %d', server_port); });<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u0437\u0430\u0442\u0435\u043c \u0438\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 <code>client.on<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f, \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443 &#171;\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435&#187;, \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430\u0437\u0430\u0434 \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. \u0412\u0430\u0448 WebSocket \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u043d\u0430 <code>http:\/\/localhost:3000<\/code>.<\/p>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm start<\/code> \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440. \u0412\u044b\u0432\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0438\u043c:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Flutter<\/h3>\n<p><strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a\u0430\u043a \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u043a \u043d\u0435\u043c\u0443. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>flutter create socket_chat<\/code>. \u041d\u0430 <a href=\"https:\/\/pub.dev\">pub.dev<\/a> \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e <strong>socket.io<\/strong> \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u043d\u043e, \u043a\u0430\u043a \u043c\u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, <a href=\"https:\/\/pub.dev\/packages\/socket_io_client\">socket_io_client<\/a> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0449\u0435 \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u043f\u0438\u0435\u0439 <em>js<\/em> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h4>\u200b \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/h4>\n<p>\u041f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a <code>localhost<\/code> \u043d\u0430 <strong>Android<\/strong> \u044d\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 <code>adb reverse tcp:3000 tcp:3000<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a <code>localhost<\/code> \u0447\u0435\u0440\u0435\u0437 <code>http:\/\/127.0.0.1:3000<\/code> \u043d\u0430 Android\u00a0(\u0430 \u0431\u0435\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <a href=\"https:\/\/developer.android.com\/studio\/run\/emulator-networking\"><u>\u043c\u043e\u0436\u043d\u043e<\/u><\/a> \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a <code>localhost<\/code> \u0441 Android \u044d\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/10.0.2.2:3000<\/code>. \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430)  <em>. <\/em>\u041f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e <em>\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f-\u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430<\/em> \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e <code>home.dart<\/code>.<\/p>\n<h4>home.dart<\/h4>\n<pre><code class=\"dart\">...     Socket socket;      @override   void initState() {     super.initState();     connectToServer();   }    void connectToServer() {     try {             \/\/ Configure socket transports must be sepecified       socket = io('http:\/\/127.0.0.1:3000', &lt;String, dynamic>{         'transports': ['websocket'],         'autoConnect': false,       });             \/\/ Connect to websocket       socket.connect();             \/\/ Handle socket events       socket.on('connect', (_) => print('connect: ${socket.id}'));       socket.on('location', handleLocationListen);       socket.on('typing', handleTyping);       socket.on('message', handleMessage);       socket.on('disconnect', (_) => print('disconnect'));       socket.on('fromServer', (_) => print(_));      } catch (e) {       print(e.toString());     }        }       \/\/ Send Location to Server   sendLocation(Map&lt;String, dynamic> data) {     socket.emit(\"location\", data);   }       \/\/ Listen to Location updates of connected usersfrom server   handleLocationListen(Map&lt;String, dynamic> data) async {     print(data);   }    \/\/ Send update of user's typing status    sendTyping(bool typing) {     socket.emit(\"typing\",       {         \"id\": socket.id,          \"typing\": typing,       });   }       \/\/ Listen to update of typing status from connected users   void handleTyping(Map&lt;String, dynamic> data) {     print(data);   }    \/\/ Send a Message to the server   sendMessage(String message) {       socket.emit(\"message\",         {           \"id\": socket.id,           \"message\": message, \/\/ Message to be sent           \"timestamp\": DateTime.now().millisecondsSinceEpoch,         },       );   }    \/\/ Listen to all message events from connected users   void handleMessage(Map&lt;String, dynamic> data) {     print(data);   }   ...<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0448\u0435 &#8212; \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f WebSocket, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CI\/CD \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h3>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 WebSocket \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f(Test, Dev, \u0438\u043b\u0438 Prod). \u0425\u043e\u0440\u043e\u0448\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u043e\u0439 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0438\u0435 \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0443\u0447\u0430\u0441\u0442\u043a\u043e\u0432 \u043a\u043e\u0434\u0430(\u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, <em>URL<\/em>-\u0430\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430) \u0432 \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e <strong>CI\/CD<\/strong> <strong>\u0441\u0431\u043e\u0440\u043a\u0443<\/strong>.<\/p>\n<blockquote>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>CI\/CD<\/strong> \u0441\u0442\u0430\u043b\u043e \u043d\u043e\u0440\u043c\u043e\u0439 \u0434\u043b\u044f \u0445\u043e\u0440\u043e\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0445 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u044b\u0445 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043e\u043d\u043e\u0433\u043e \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 <strong>Flutter<\/strong> \u043f\u0430\u043a\u0442\u043e\u0432, <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e.<\/p>\n<\/blockquote>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u0440\u0435\u0434\u044b <strong>CI\/CD<\/strong> \u0432 Codemagic. \u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0439\u0442\u0438 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c, \u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c \u0438 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0440\u0435\u0434\u044b <code>dev<\/code> \u0438 <code>prod<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e <code>.yaml<\/code> \u0444\u0430\u0439\u043b\u0430 \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0441\u0432\u043e\u0439 <a href=\"https:\/\/codemagic.io\/start\/\">Codemagic.io<\/a> \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u043a\u0440\u0430\u0442\u0447\u0430\u0439\u0448\u0438\u0435 \u0441\u0440\u043e\u043a\u0438.<\/p>\n<p>\u0414\u0430, \u044f \u0437\u043d\u0430\u044e, \u0447\u0442\u043e \u0435\u0441\u0442\u044c UI, \u043d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0442\u043e\u043a CI\/CD, \u043d\u0435 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435, \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0441 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c. \u0418\u0442\u0430\u043a, \u0432\u043e\u0442 \u0431\u044b\u0441\u0442\u0440\u044b\u0439, \u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e:<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u0421\u0440\u0435\u0434\u044b<\/h4>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <strong>CI\/CD<\/strong> \u0438 \u0441\u0440\u0435\u0434\u044b \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 Codemagic, \u0442\u0430\u043a \u0438 \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u0435\u0434\u044b \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0441\u0442\u0430, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043d\u0430\u0448 \u043d\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 <strong>JSON<\/strong> \u0444\u0430\u0439\u043b <code>config\/appconfig.json<\/code>, \u0432 \u043d\u0435\u043c \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u044b \u043d\u0430\u0448\u0438 <em>URL<\/em>-\u0430\u0434\u0440\u0435\u0441\u0430 \u0445\u043e\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438. \u0412\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c.<\/p>\n<pre><code class=\"yaml\">assets:   - config\/app_config.json<\/code><\/pre>\n<p>\u041a\u043e\u0434 \u0432\u00a0<code>lib\/config\/config_loader.dart<\/code>\u00a0\u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e <strong>JSON<\/strong> \u0444\u0430\u0439\u043b\u0430\u00a0<code>(config\/appconfig.json)<\/code>. <\/p>\n<p><code>env.dart<\/code>\u00a0\u0438\u00a0<code>env_config.dart<\/code>\u00a0\u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0438\u0445 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0440\u0435\u0434 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432<code>_dev.dart<\/code>\u00a0\u0438\u00a0<code>_prod.dart<\/code>\u00a0.  <\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 <strong>Flutter<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439\u00a0<code>main.dart<\/code>\u00a0\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439. \u041d\u043e \u0434\u043b\u044f \u043d\u0430\u0441, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0443\u043c\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430, \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b\u00a0<code>main_common.dart<\/code>\u00a0,\u00a0<code>main_dev.dart<\/code>\u00a0\u0438\u00a0<code>main_prod.dart<\/code>. \u041a\u0430\u043a \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f,\u00a0<code>main_common.dart<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u043c\u043e\u0441\u0442\u043e\u043c, \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u043c \u0441 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0439 \u0441\u0440\u0435\u0434\u043e\u0439. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c\u00a0<code>flutter run -t lib\/main_dev.dart<\/code>\u00a0\u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u00a0<code>flutter run -t lib\/main_prod.dart<\/code>\u00a0\u0434\u043b\u044f \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<h4>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <strong>JSON<\/strong> \u043a\u043e\u043d\u0444\u0438\u0433 \u0432 <em>base64<\/em>, \u0447\u0442\u043e\u0431\u044b \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b \u0432 <code>codemagic.yaml<\/code>, <a href=\"https:\/\/docs.codemagic.io\/getting-started\/yaml\/\">\u0437\u0434\u0435\u0441\u044c <\/a>\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u044d\u0442\u043e\u043c.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h4>Codemagic.yaml<\/h4>\n<p>Codemagic \u0434\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u043e\u043c <code>yaml<\/code> <a href=\"https:\/\/docs.codemagic.io\/getting-started\/yaml\/\">\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0435<\/a>. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043d\u0430\u0448 \u0444\u0430\u0439\u043b <code>codemagic.yaml<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"yaml\"># Automatically generated on 2020-11-16 UTC from https:\/\/codemagic.io\/app\/5fb2a25c605096f91720b983\/settings # Note that this configuration is not an exact match to UI settings. Review and adjust as necessary.  workflows:   default-workflow:     name: Default Workflow     max_build_duration: 60     environment:       vars:         APP_CONFIG: ewogICJnQXBpS2V5IjogIkFJemFTeUEzbl95bTlWUUU2NURyRUVpdDZobnNtWDgyR3FGb3Q0QSIKfQo=       flutter: stable       xcode: latest       cocoapods: default     scripts:       - |         # set up debug keystore         rm -f ~\/.android\/debug.keystore         keytool -genkeypair \\           -alias androiddebugkey \\           -keypass android \\           -keystore ~\/.android\/debug.keystore \\           -storepass android \\           -dname 'CN=Android Debug,O=Android,C=US' \\           -keyalg 'RSA' \\           -keysize 2048 \\           -validity 10000               - |         # set up local properties         echo \"flutter.sdk=$HOME\/programs\/flutter\" > \"$FCI_BUILD_DIR\/android\/local.properties\"               - cd . &amp;&amp; flutter packages pub get       - |         # Create directory if it doesn't exist         mkdir -p $FCI_BUILD_DIR\/config         # Write out the<\/code><\/pre>\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-386529","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/386529","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=386529"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/386529\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=386529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=386529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=386529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}