1

Okay so I'm tired of trying to figure out how to do this. I can't find a tutorial or article or anything that explains how to do it.

I want to make an app where a device creates a connection and other devices nearby can connect to it. Some good examples of what I want to implement are Pou and XConnectedApps apps.

Pou has some minigames where you can play with your friends near you either by bluetooth or mobile hotspot. One player creates the game (or connection) or opens a room and the other players choose this room from a list that scans the opened rooms.

XConnectedApps has 2 apps: Barcode Reader and Universal Clipboard. Both work the same way. You install a small app on your PC which starts a server when run and you install the other apps on the phone. The android app can scan for the servers on the same network and connect to one of them. Once the connection is complete, you can start sending and receiving data.

I read somewhere that I should start a server using HttpServer and use the http package to connect to it and send requests. So this is what I did:

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();

  late HttpServer _server;
  bool _serverRunning = false;

  @override
  void dispose() {
    _controller.dispose();
    _server.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Form(
              child: TextFormField(
                controller: _controller,
                decoration: const InputDecoration(labelText: 'Send a message'),
              ),
            ),
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                FloatingActionButton(
                  onPressed: _serverRunning ? _stopServer : _startServer,
                  tooltip: _serverRunning ? "Stop Server" : "Start Server",
                  child: Icon(_serverRunning ? Icons.stop : Icons.play_arrow),
                ),
                FloatingActionButton(
                  onPressed: _sendMessage,
                  tooltip: "Send Message",
                  child: Icon(Icons.send),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _startServer() async {
    final info = NetworkInfo();
    var wifiIP = await info.getWifiIP();

    _server = await HttpServer.bind(InternetAddress(wifiIP!), 8080);
    print("server running on ${_server.address}");
    setState(() {
      _serverRunning = true;
    });

    _server.listen((HttpRequest request) {
      request.response.write("Hello World!");
      request.response.close();
    });
  }

  Future<void> _stopServer() async {
    await _server.close();
    setState(() {
      _serverRunning = false;
    });
  }

  Future<void> _sendMessage() async {
    if (_controller.text.isEmpty) {
      return;
    }

    var response = await http.post(
      Uri.parse("http://192.168.1.10:8080"),
      body: {
        "name": "User",
        "data": _controller.text,
      },
    );
    
    print("response status: ${response.statusCode}, body: ${response.body}");
  }
}

This shows a text field which contains the message that will be sent and 2 buttons: one to start/stop the server and another one to send the message.

On desktop, I start a server using the same machine's IP address and I was able to access the webpage on any device on the same LAN (just to see if it works) and I was able to see the Hello World! message passed in request.response.write("Hello World!") in the body of the listen() function. But what I was not able to do and I don't think if I can do this is to receive the data passed in the body of the POST request at the end of this code. The hardcoded IP is the desktop's IP on the network.

As you can see, I though I could start a server on my PC and using my Android phone I can send a request to the server's IP address with some data in the body. But I'm not able to access that data on the server side when listening for requests.


Anyway, I thought of another way to do it, thinking it is easier; using Bluetooth. I found this package which supports Windows and Android. So I changed my code to this:

class _HomeScreenState extends State<HomeScreen> {
  final _controller = TextEditingController();

  bool _scanRunning = false;

  List<String> _infoList = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            TextFormField(
              controller: _controller,
              decoration: const InputDecoration(labelText: 'Send a message'),
            ),
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                FloatingActionButton(
                  onPressed: _scanRunning ? _stopScan : _startScan,
                  tooltip: _scanRunning ? "Stop Scan" : "Start Scan",
                  child: Icon(_scanRunning ? Icons.stop : Icons.play_arrow),
                ),
                FloatingActionButton(
                  onPressed: _sendMessage,
                  tooltip: "Send Message",
                  child: Icon(Icons.send),
                ),
              ],
            ),
            const SizedBox(height: 24),
            ..._infoList.map(
              (info) => Text(info),
            ),
          ],
        ),
      ),
    );
  }

  void _addMessage(String message) {
    setState(() {
      _infoList.add(message);
    });
  }

  Future<void> _startScan() async {
    bool isAvailable = await QuickBlue.isBluetoothAvailable();
    if (!isAvailable) {
      _addMessage("Bluetooth not supported on this device");
    } else {
      _addMessage("Buetooth is on");
    }

    QuickBlue.startScan();
    _addMessage("Scanning devices....");
    setState(() {
      _scanRunning = true;
    });

    QuickBlue.scanResultStream.listen((result) {
      String message = "ID: ${result.deviceId}\nName: ${result.name}";

      if (_infoList.contains(message)) {
        setState(() {
          _infoList.removeWhere((element) => element == message);
        });
      } else {
        _addMessage(message);
      }
    });
  }

  void _stopScan() {
    QuickBlue.stopScan();
    _addMessage("Scan stopped");
    setState(() {
      _scanRunning = false;
    });
  }

  void _sendMessage() {
    
  }
}

If bluetooth is turned off it shows that it is not available. Not if the device supports bluetooth. I wanted it to show a list of devices that have bluetooth turned ON, similar to the one in bluetooth settings page. If a device has bluetooth turned on, it is shown in the list. Otherwise it is removed.

Using this code, I had a lot of items in the list blinking, cause they kept being added and removed, I don't know why. The name is shown either blank or some kind of MAC address or something. And the device id always changes each time bluetooth is turned ON and OFF.

I didn't know how to continue with this code because I don't think I am on the correct path. So I'm asking you guys. Does anyone know how can I do this kind of stuff? I've been struggling with this for many months. Any help would be really appreciated. Thank You for your time!

Guest
  • 11
  • 1
  • I tried your code with wifi connecting to connect my mac to my windows. It works. I just had to add [this](https://stackoverflow.com/a/65866640/15237377) to the mac app. – Schaban Nov 01 '22 at 12:05
  • @Schaban yes but what is the correct way of creating the connection between 2 devices (similar to the examples I gave)? that's my original question – Guest Nov 01 '22 at 17:34
  • Hi, I think you can use this p2p package: https://pub.dev/packages/flutter_nearby_connections – Shawn Dec 27 '22 at 21:07

0 Answers0