How to integrate Flutter with Phoenix?

Hello everyone,
I come from a Java background, and I recently started learning Elixir. I still didn’t look into Phoenix or Flutter, but my idea would be to release a web app that uses these tools in the following months.
I wanted to know if this is possible and also recommended to use Flutter alongside Phoenix. I tried to search on Google how to integrate these two frameworks, but I did not find anything helpful.
Can/should I use Flutter also for the browser version? Or would it be better to implement that with e.g. Vue? PS: the focus goes more on Android and iOS versions, but it should also have good browser support.
Thank you in advance.

1 Like

:wave: Hello and welcome!
You can do the same as you would do on Java world, have controllers that serves json responses forming an API, Phoenix can be agnostic about which view technology you will use be it react, flutter, Vue, jQuery. You can use the same API you end up developing for both mobile and web. You can certainly use flutter with it, AFAIK Flutter web is stable so you can develop for mobile and web with it!

PS: You might find more examples on the web on how to setup Vue and React with Phoenix instead of Flutter because of popular usage, but they follow the same principle.

I don’t know Flutter but Phoenix can be used only as a backend – if you really want to use any other technology for the frontend.

If Flutter also covers the backend then no, you’d have a very hard trying to make two backend frameworks work together.

If You want to use websocket and Phoenix channels, it is important to find a client…

There is one in JS coming with the framework, or from Npm.

There seems to be one for Dart… but have not tried it.

You could also use Absinthe to run a GraphQL server with Phoenix and access it with GraphQL-Flutter. If were building a Flutter app right now that’s the route I would go.

1 Like

I used phoenix_wings in a proof of concept Android app some time ago, it’s quite straightforward to implement a channels client in flutter with it, served by a Phoenix backend. I could dig my HD and publish a github repo, even if I’m totally noob with Dart an Flutter, if you’re interested.

P. S. Sorry kokolegorille, I didn’t meen to reply to you, but to the OP

2 Likes

Hey. Please can you share your flutter/phoenix repo?

Hi @AliaksandrBadretdzin, I’m posting here the two only significative files, Flutter’s main.dart and Phoenix’channel module. All the remaining files are just standard defaults in both projects. The Flutter client connects to a phoenix channel, send a message with no payload, and receive a broadcasted message in return then updates its UI with a purring audio feedback. It was one of my very first silly experiments with Flutter. Hth.

main.dart

import 'package:flutter/material.dart';
import 'package:phoenix_wings/phoenix_wings.dart';
import 'package:audioplayers/audio_cache.dart';
import 'dart:math';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Mimitos!',
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: MyHomePage(title: 'Mimitos!'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;
  final socket = PhoenixSocket("ws://192.168.1.106:4000/socket/websocket");
  final AudioCache player = new AudioCache();
  final purrAudioPath = "purr.mp3";
  final faces = ["😫", "😩", "😣", "😔", "😑", "😌", "😊"];

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  PhoenixChannel _channel;
  int _happyness;

  void initState() {
    _happyness = 0;
    _connectSocket();
    super.initState();
  }

  Future<void> _connectSocket() async {
    await widget.socket.connect();

    _channel = widget.socket.channel("room:mimitos");
    _channel.on("mimitos!", _incrementHappyness);

    _channel.join();
  }

  void _incrementHappyness(_payload, _ref, _joinRef) {
    widget.player.play(widget.purrAudioPath);

    if (_happyness < 26) {
      setState(() {
        _happyness = _happyness + 1;
      });
    } else {
      setState(() {
        var rng = new Random();
        _happyness = rng.nextInt(10) + 5;
      });
    }
  }

  void _sendMimitos() {
    _channel.push(event: "send_mimitos!", payload: {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: Container(
              alignment: Alignment.center,
              color: Colors.deepPurple,
              child: Text(widget.faces[_happyness ~/ 4],
                  style: TextStyle(
                      fontFamily: 'Raleway',
                      fontSize: 156,
                      decoration: TextDecoration.none,
                      color: Colors.white)))),
      floatingActionButton: new FloatingActionButton(
        onPressed: _sendMimitos,
        tooltip: 'Send Mimitos!',
        child: new Icon(Icons.pets),
      ),
    );
  }
}

room_channel.ex

defmodule MimitosServerWeb.RoomChannel do
  use Phoenix.Channel

  def join("room:mimitos", _message, socket) do
    {:ok, socket}
  end

  def join("room:" <> _private_room_id, _params, _socket) do
    {:error, %{reason: "unauthorized"}}
  end

  def handle_in("send_mimitos!", %{}, socket) do
    broadcast!(socket, "mimitos!", %{})
    {:noreply, socket}
  end
end