I'm trying to learn Webdev and as part of my first project, I have built a simple chat-bot-app. The app consists of a Python-Flask backend (running on localhost/5000) and a Flutter app front end. The Flutter app takes our input, sends it to the Flask server, and will print us the response.
When I run the app in debug mode locally, it is working fine as expected. However, when I build a web version of the app using
flutter build web
and then try to run the app from /build/web/ by creating a Python server
python -m http.server 8081
There is a problem, the web app is launching in the browser: however, now it can not send and receive messages from the server running at localhost/5000. I'm getting an error:
flutter.js:368 Exception while loading service worker: Error: Service Worker API unavailable.
The current context is NOT secure.
Read more: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
at FlutterServiceWorkerLoader.loadServiceWorker (flutter.js:130:11)
at FlutterLoader.loadEntrypoint (flutter.js:366:33)
at (index):46:23
(anonymous) @ flutter.js:368
0.0.0.0/:1 Access to XMLHttpRequest at 'http://127.0.0.1:5000/api/chat' from
origin 'http://0.0.0.0:8081' has been blocked by CORS policy:
The request client is not a secure context and the resource is in more-private address space `local`.
main.dart.js:6506 Uncaught Error
at Object.alx (main.dart.js:6573:24)
at TE.$1 (main.dart.js:73160:53)
at abl.adR (main.dart.js:36153:34)
at abl.DP (main.dart.js:36155:21)
at a95.$0 (main.dart.js:35896:11)
at Object.r4 (main.dart.js:5825:40)
at az.mx (main.dart.js:35827:3)
at Object.ayw (main.dart.js:5908:8)
at a4N.$1 (main.dart.js:35938:9)
at a8N.$1 (main.dart.js:39265:21)
127.0.0.1:5000/api/chat:1 Failed to load resource: net::ERR_FAILED
It basically says I can not communicate with 'http://127.0.0.1:5000/api/chat' from origin 'http://0.0.0.0:8081' due to CORS security policy. When I try to run the same web app, by disabling the browser security, I'm no longer getting this error and the web app is able to communicate with the server.
chromium-browser --disable-web-security
How can I fix this without sacrificing security? I wish to deploy the app online after fixing this.
Why is this happening? Here is the link to the Github repo which has the full code: https://github.com/sri-gpt/simple_chat_app
I'm using:
- Ubuntu version 22.04 LTS,
- Flutter 3.13.0-0.2.pre
- Dart 3.1.0
- DevTools 2.25.0
- Flask 2.3.2
- Flask-Cors 4.0.0
- Python 3.9
Sample code: Python Flask server
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/')
def home():
return 'ServerDEployed'
@app.route('/about')
def about():
return 'About'
@app.route('/api/chat', methods=['POST'])
def chat():
data = request.get_json()
input_text = data.get('input_text')
return jsonify({"response_mal": str("response_recived:")+str(input_text)})
if __name__ == '__main__':
app.run(debug=True)
Here is the main.dart file of the Flutter app:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const ChatApp());
}
class ChatApp extends StatelessWidget {
const ChatApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat_app',
theme: ThemeData(
primarySwatch: Colors.indigo, // Change primary color
fontFamily: 'Montserrat', // Add custom font (Montserrat can be replaced with your preferred font)
),
home: const MyHomePage(title: 'Chat_app'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _messages = <Map<String, dynamic>>[];
final _controller = TextEditingController();
final double fontText = 24.0; // <-- This is your pseudo variable for font size
Future<void> _sendText() async {
if (_controller.text.isEmpty) {
return;
}
setState(() {
_messages.add({"text": _controller.text, "type": "outgoing"});
});
final url = Uri.parse('http://127.0.0.1:5000/api/chat');
final response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'input_text': _controller.text,
}),
);
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body);
setState(() {
_messages.add({"text": data["response_mal"], "type": "incoming"});
_controller.text = "";
});
} else {
throw Exception('Failed to send message');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title, style: TextStyle(fontSize: fontText)), // <-- Apply font size here.
),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: _messages.length,
itemBuilder: (_, int index) {
final message = _messages[index];
final isOutgoing = message["type"] == "outgoing";
return Container(
margin: const EdgeInsets.symmetric(vertical: 5),
padding: const EdgeInsets.all(10),
alignment:
isOutgoing ? Alignment.centerRight : Alignment.centerLeft,
child: Text(
message["text"],
style: TextStyle(fontSize: fontText), // <-- Apply font size here.
),
decoration: BoxDecoration(
color: isOutgoing ? Colors.orange[100] : Colors.blue[100],
borderRadius: BorderRadius.circular(5),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
);
},
),
),
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
onSubmitted: (_) => _sendText(),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your message',
),
style: TextStyle(fontSize: fontText), // <-- Apply font size here.
),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: ElevatedButton( // Change the send button to ElevatedButton
onPressed: _sendText,
child: Icon(Icons.send),
),
),
],
),
],
),
);
}
}