Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

way to run existing flutter code #161

Open
dvc94ch opened this issue Mar 27, 2022 · 4 comments
Open

way to run existing flutter code #161

dvc94ch opened this issue Mar 27, 2022 · 4 comments

Comments

@dvc94ch
Copy link
Contributor

dvc94ch commented Mar 27, 2022

nativeshell supports creating desktop applications using the WindowWidget and WindowLayoutProbe. is there a way to get the regular Scaffold based apps to work with nativeshell?

@knopp
Copy link
Contributor

knopp commented Mar 27, 2022

I'm not quite sure what you mean. You can still use Scaffold if you put it inside WindowLayoutProbe, i.e.

   WindowWidget
     MaterialApp
       WindowLayoutProbe
          Scaffold

@dvc94ch
Copy link
Contributor Author

dvc94ch commented Mar 27, 2022

Ah had some issues with putting the WindowWidget inside the MaterialApp. But would be nice to be able to run a mobile flutter app on the desktop without any modifications.

@dvc94ch
Copy link
Contributor Author

dvc94ch commented Mar 27, 2022

To elaborate, I'm currently working on the default template for x which is due for release next week. It looks like this:

lib/main.dart

import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:nativeshell/nativeshell.dart';
import './bindings.dart';

void main() {
  final title = 'FlutterDemo';
  final home = CounterPage(state: Api.load().createCounterState());
  final app = Platform.isAndroid || Platform.isIOS
      ? MaterialApp(
          title: title,
          home: home,
        )
      : WindowWidget(onCreateState: (initData) {
          WindowState? state;
          state ??= MainWindow(
            title: title,
            home: home,
          );
          return state;
        });
  runApp(app);
}

class CounterPage extends StatelessWidget {
  const CounterPage({Key? key, required this.state}) : super(key: key);

  final CounterState state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('counter page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            StreamBuilder(
              stream: state.subscribe(),
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                final counter = state.counter();
                return Text('$counter',
                    style: Theme.of(context).textTheme.headline4);
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: state.increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class MainWindow extends WindowState {
  MainWindow({required this.title, required this.home});

  final String title;
  final Widget home;

  @override
  WindowSizingMode get windowSizingMode =>
      WindowSizingMode.atLeastIntrinsicSize;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: title,
      home: WindowLayoutProbe(
        child: home,
      ),
    );
  }
}

src/lib.rs

use futures::{Stream, StreamExt};
use futures::channel::mpsc;

ffi_gen_macro::ffi_gen!("api.rsh");

pub fn create_counter_state() -> CounterState {
    Default::default()
}

#[derive(Default)]
pub struct CounterState {
    counter: u32,
    subscribers: Vec<mpsc::Sender<()>>,
}

impl CounterState {
    pub fn increment(&mut self) {
        self.counter += 1;
        self.subscribers.retain(|tx| match tx.clone().try_send(()) {
            Ok(()) => true,
            Err(err) if err.is_full() => true,
            Err(_) => false,
        });
    }

    pub fn counter(&self) -> u32 {
        self.counter
    }

    pub fn subscribe(&mut self) -> impl Stream<Item = i32> {
        let (tx, rx) = mpsc::channel(1);
        self.subscribers.push(tx);
        rx.map(|_| 0)
    }
}

src/main.rs

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
fn main() {
    use nativeshell::{
        codec::Value,
        shell::{exec_bundle, register_observatory_listener, Context, ContextOptions},
    };


    exec_bundle();
    register_observatory_listener("app_template".into());

    env_logger::builder().format_timestamp(None).init();

    let context = Context::new(ContextOptions {
        app_namespace: "AppTemplate".into(),
        ..Default::default()
    });

    let context = context.unwrap();

    context
        .window_manager
        .borrow_mut()
        .create_window(Value::Null, None)
        .unwrap();

    context.run_loop.borrow().run();
}
x run
[1/8] Fetch flutter repo [SKIPPED]
[2/8] Fetch precompiled artefacts
[2/8] Fetch precompiled artefacts [13ms]
[3/8] Run pub get [SKIPPED]
[4/8] Build rust
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
[4/8] Build rust [124ms]
[5/8] Build flutter assets [0ms]
[6/8] Build kernel_blob.bin [SKIPPED]
[7/8] Build aot snapshot [SKIPPED]
[8/8] Create appdir [19ms]

(helloworld:971938): dbind-WARNING **: 21:32:56.188: Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-bHi7D9kf0m: No such file or directory

** Help me make NativeShell and Flutter on desktop better!
** We have a long way to go: https://nativeshell.dev/roadmap

nativeshell: Writing VM Service info file into ${XDG_RUNTIME_DIR}/vmservice.app_template

Syncing files to device Linux...                                    7.0s

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

💪 Running with sound null safety 💪

An Observatory debugger and profiler on Linux is available at: http://127.0.0.1:44235/Hbm47SafYIc=/
Lost connection to device.

@knopp
Copy link
Contributor

knopp commented Mar 27, 2022

To be honest I haven't tried LayoutProbe outside of Scaffold. It is quite possible that it won't work due to scaffold having some widgets that will not work in unconstrained environment and thus can not be sized.

Good thing is WindowLayoutProbe should be optional. It is only required if windowSizingMode is atLeastIntrinsicSize or sizeToContents. If you size your window manually, you can return WindowSizingMode.manual from windowSizingMode and not use layout probe. You can then set initial size in initializeWindow.

It's certainly possible to share the code between desktop and mobile (we're doing it for Superlist). The only issue here is that if you want to use Scaffold on desktop you'll not be able to use the layout probe and have automatic window sizing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants