Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
1l0 committed Jan 16, 2023
0 parents commit e191fd9
Show file tree
Hide file tree
Showing 13 changed files with 403 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.dart_tool
.flutter-plugins
.flutter-plugins-dependencies
.packages
/build
/ios
/macos
.DS_Store
/pubspec.lock
go.sum
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# flap

<p align="center">
<img width="150" height="150" src="https://raw.githubusercontent.com/capitalpidx/flap/master/art/logo.png"><br/>
Working proof of the Go server running inside Flutter
</p>

[Video in action](https://www.youtube.com/watch?v=4DPL5F2DVdc)

## Prerequisites

- [Flutter](https://flutter.dev) >2.0
- [Go](https://golang.org) >1.16

## Build Go server
```
cd go
```
macOS:
```
make macos
```
iOS:
```
make ios
```
iOS Simulator:
```
make ios-simulator
```

## Run app
```
cd ..
flutter devices
flutter run -d {target}
```

## Build app
```
flutter build {macos, ios}
```


## Known issues

- Hot reload doesn't work (workaround: run Go server independently in the development phase)

## Q&A

### Why .dylib instead of .a as an iOS library?
Because I can despite official docs prefer .a. In fact I haven't even tried .a.

## Contribution

I'd like to see stuff working on other platforms.
Binary file added art/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions flap.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
2 changes: 2 additions & 0 deletions go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go.sum
testdata/
11 changes: 11 additions & 0 deletions go/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Todo: fix that these don't work on the apple silicon

all: macos ios

macos:
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -buildmode=c-shared -o ../macos/server-macos.dylib main.go
ios-simulator:
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 SDK=iphonesimulator CC=`go env GOROOT`/misc/ios/clangwrap.sh go build -tags ios -buildmode=c-shared -o ../ios/server-ios.dylib main.go
ios:
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 SDK=iphoneos CC=`go env GOROOT`/misc/ios/clangwrap.sh CGO_CFLAGS="-fembed-bitcode" go build -tags ios -buildmode=c-shared -o ../ios/server-ios.dylib main.go

5 changes: 5 additions & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/errtokyo/flap/go

go 1.16

require github.com/cockroachdb/pebble v0.0.0-20210423210359-b62f76615457
20 changes: 20 additions & 0 deletions go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//go:build !detached

package main

import (
"strconv"

"github.com/1l0/flap/go/server"
)

import "C"

//export serve
func serve(port C.int, dir *C.char) {
p := strconv.Itoa(int(port))
d := C.GoString(dir)
server.Serve(p, d)
}

func main() {}
13 changes: 13 additions & 0 deletions go/main_detached.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build detached

package main

import (
"github.com/1l0/flap/go/server"
"log"
)

func main() {
log.Println("Listen on http://localhost:9090")
server.Serve("9090", "~/Downloads/")
}
41 changes: 41 additions & 0 deletions go/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package server

import (
"fmt"
"net/http"
"time"

"github.com/cockroachdb/pebble"
)

func Serve(port, libraryPath string) {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
db, err := pebble.Open(libraryPath+"db", &pebble.Options{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
key := []byte("now")
if err := db.Set(key, []byte(time.Now().String()), pebble.Sync); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
value, closer, err := db.Get(key)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
dbres := fmt.Sprintf("%s is %s", key, value)
if err := closer.Close(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := db.Close(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Hello from Go\n\nport: %s\nfrom local DB:\n%s\n", port, dbres)
})
http.ListenAndServe(":"+port, mux)
}
118 changes: 118 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:ffi' as ffi;
import 'package:path_provider/path_provider.dart';
import 'package:ffi/ffi.dart';
import 'dart:developer';
import 'dart:isolate';
import 'package:http/http.dart' as http;

void main() {
runApp(MyApp());
}

class Config {
static const bool localDev = false;
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'local Go server'),
);
}
}

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

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

typedef serve_func = ffi.Void Function(ffi.Int32, ffi.Pointer<Utf8>);
typedef Serve = void Function(int, ffi.Pointer<Utf8>);

class _MyHomePageState extends State<MyHomePage> {
int _port = 9090;
bool _running = false;
String _body = "";

void _startServer() async {
if (_running || Config.localDev) return;
Directory supportDir = await getApplicationSupportDirectory();
String dir = supportDir.path;
log('application support directory: $dir');
int p = await getUnusedPort();
setState(() {
_port = p;
_running = true;
});
await Isolate.spawn(_launchServer, [p, dir]);
}

static void _launchServer(List<Object> args) async {
int p = args[0];
String dir = args[1];

final dylib = () {
if (Platform.isIOS) {
return ffi.DynamicLibrary.open('server-ios.dylib');
}
return ffi.DynamicLibrary.open('server-macos.dylib');
}();
final Serve serve =
dylib.lookup<ffi.NativeFunction<serve_func>>('serve').asFunction();
serve(p, dir.toNativeUtf8());
log('finished go server');
}

Future<int> getUnusedPort() async {
ServerSocket socket = await ServerSocket.bind(
InternetAddress.loopbackIPv4 ?? InternetAddress.anyIPv4, 0);
var port = socket.port;
socket.close();
log('go server listening on port: $port');
return port;
}

void _fetch() async {
final res = await http.get(Uri.http('localhost:$_port', '/'));
if (res.statusCode == 200) {
setState(() {
_body = res.body;
});
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (_running || Config.localDev) Text('server port: $_port'),
if (_running || Config.localDev)
TextButton(onPressed: _fetch, child: Text('fetch')),
Text('$_body'),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _startServer,
tooltip: 'Launch server',
child: Icon(Icons.launch),
),
);
}
}
79 changes: 79 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: flap
description: A new Flutter project.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
sdk: ">=2.7.0 <3.0.0"

dependencies:
flutter:
sdk: flutter
ffi:
path_provider:
http:


# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2

dev_dependencies:
flutter_test:
sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true

# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages

# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

0 comments on commit e191fd9

Please sign in to comment.