Brother Cat said
Seeing this picture, I feel sore in my neck. . . I posed like this, although it looks cool, and then your neck needs to be adjusted up and down, which is more troublesome than adjusting left and right.
Today I recommend reading about Flutter desktop development. Below is the original text and code Git link.
Before I started, I remembered a sentence that React Native said "Use the same method for multi-end development". This sentence is very mysterious and correct. It means that the same set of code can be compiled into ios, android, web, windows, linux at the same time... , It's just a high degree of code reuse, so some students should pay attention to unpacking when doing multi-terminal architecture.
For example, your business code, API, Entity, and functional functions are reused in many cases. But the bottom layer and interactive experience of each platform is personalized.
Don't be fooled by the smooth compatibility on both ends of ios and android.
Today we introduce NativeShell, a desktop program. This program demonstrates examples of multi-windows, modal dialogs, drag and drop, menus, toolbars, file operations, and system API calls. If you are doing research, this is a good reference.
Old iron remember to forward, Brother Mao will present more Flutter good articles~~~~
WeChat group ducafecat
original
https://matejknopp.com/post/introducing-nativeshell/
video
Code
https://github.com/nativeshell/app_template
reference
- http://airflow.app/
- https://airflow.app/remote-app/
- https://github.com/nativeshell/examples/blob/main/src/file_open_dialog.rs#L18
- https://github.com/nativeshell/examples/blob/main/lib/pages/drag_drop.dart
- https://github.com/nativeshell/nativeshell/blob/main/nativeshell/src/shell/platform/win32/drag_com.rs
text
Ever since I first saw Turbo Vision, I have been interested in desktop applications. Those text-mode resizable windows in DOS are like magic to me. It stimulated people's interest in user interface frameworks, which are still very powerful after more than 20 years.
In the past ten years or so, people's attention has mainly shifted to the Internet and mobile devices, which has not made me particularly happy. So I felt it was time to climb out of the shadows, leave my comfort zone, and try to bring some spotlights back where it should be. Go to the desktop! :)
Flutter road
The last desktop application I dealt with was (and still is) Airflow. It is a mixture of Qt and a lot of platform-specific code. If I say this myself, I am very satisfied with the final result, but there is still much room for improvement in the developer experience and overall productivity.
About two years ago, I needed an airflow app for iOS and Android. After a few prototypes, the decision was made and I went to Flutter. I do like to think that I have my own UI development experience. After all, I have used more than a dozen GUI frameworks on different platforms, so there is nothing that surprises me now, right? wrong. The biggest surprise of all of them is how good it feels to work with Flutter. In my life, never, never once, building a user interface makes sense.
https://airflow.app/remote-app/
Wouldn’t it be amazing if I could build desktop applications in this way? Of course, this is possible, but the reality is a harsh mistress, at that time desktop embedding was still in its infancy. Then go back to Qt. But this idea has been lingering in my mind.
One or two years have passed, and many things have changed. There is still a lot of work to be done, but desktop embedding has matured a lot, and Flutter on desktop has begun to become a viable option.
Flutter desktop embed
Now you may ask: Matt, isn’t Flutter already embedded in the desktop? So what is all this for?
Yes, it is true. NativeShell is built on them. You can think of the Flutter desktop embedding program as a platform view component (think GtkWidget, NSView or, IMHO, HWND). It handles mouse and keyboard input, drawing, but it does not try to manage windows, or Flutter engine/isolation. Or do things like platform menus and drag and drop. To make things more complicated, Flutter has completely different APIs embedded on each platform. Therefore, if you want to create engines or register platform channel handlers for some low-level code, you need to do this separately for each platform.
This is where NativeShell gets involved
Start where the embedding of the Flutter desktop ends. It provides a consistent, platform-independent API for existing Flutter embeddings. It manages the engine and windows. It provides drag-and-drop support, access to platform menus, and other features beyond the scope of Flutter's embedding. And it exposes all of these through the easy-to-use Dart API.
NativeShell is written in rust. Rust is great because it allows you to write efficient low-level platform specific code if you need it, but it also lets you use NativeShell without having to know any Rust. Simply performing cargo transportation is all you need to keep things going. Cargo is the Rust package manager (just like pub is for Dart), it is responsible for downloading and building all dependencies.
begin
- Install Rust
https://www.rust-lang.org/tools/install - Install Flutter
https://flutter.dev/docs/get-started/install - Enable desktop support in Flutter (choose one for your platform)
$ flutter config --enable-windows-desktop
$ flutter config --enable-macos-desktop
$ flutter config --enable-linux-desktop
- Switch to Flutter Master
$ flutter channel master
$ flutter upgrade
After this, you should be able to start:
$ git clone https://github.com/nativeshell/examples.git
$ cd examples
$ cargo run
NativeShell transparently integrates the Flutter build process and cargo. If the god of rust and darts is smiling at you, this is what you should see now:
Platform Channels
If you need to call native code from a Flutter application, the two options are platform channel or FFI. The commonly used platform channels are pre-designed because they are easier to use and can properly pass messages between the platform and the UI thread.
This is the effect of using NativeShell to register the platform channel handler (I promise, here is the only Rust code)
fn register_example_channel(context: Rc<Context>) {
context
.message_manager
.borrow_mut()
.register_method_handler("example_channel", |call, reply, engine| {
match call.method.as_str() {
"echo" => {
reply.send_ok(call.args);
}
_ => {}
}
});
}
In order to directly use the existing platform embedded API to complete this work, you need to use platform-specific APIs to write these codes for each platform separately. Then make sure that the handler is registered every time a new engine is created (it may be unregistered when the engine is shut down).
With NativeShell, you only need to register the handler once, and it can be called from any engine. Messages can be transparently serialized and deserialized into Rust structure through Serde (using Flutter's standard encoding and decoding format).
https://github.com/nativeshell/examples/blob/main/src/file_open_dialog.rs#L18
Window Management
Presumably you want your desktop application to have multiple windows? NativeShell has covered you. Adjust the window size to the content or set the minimum window size so that the layout of Flutter is not bottomless? It can also do this. It also ensures that the window is only displayed when the content is ready, thereby eliminating ugly flickering.
Currently, each window operates as a separate independent form. NativeShell provides APIs for creating windows, setting and adjusting geometric shapes, updating styles and window titles. It also provides an API to facilitate communication between windows.
The video can be resized according to the content, and can also be resized according to the size of the content.
- Multi-window
- Modal dialog
This will be a minimal demonstration of how to create and manage multiple windows in Dart:
void main() async {
runApp(MinimalApp());
}
class MinimalApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Widgets above WindowWidget will be same in all windows. The actual
// window content will be determined by WindowState
return MaterialApp(
home: WindowWidget(
onCreateState: (initData) {
WindowState? context;
context ??= OtherWindowState.fromInitData(initData);
// possibly no init data, this is main window
context ??= MainWindowState();
return context;
},
),
);
}
}
class MainWindowState extends WindowState {
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () async {
// This will create new isolate for a window. Whatever is given to
// Window.create will be provided by WindowWidget in new isolate
final window = await Window.create(OtherWindowState.toInitData());
// you can use the window object to communicate with newly created
// window or register handlers for window events
window.closeEvent.addListener(() {
print('Window closed');
});
},
child: Text('Open Another Window'),
);
}
}
class OtherWindowState extends WindowState {
@override
Widget build(BuildContext context) {
return Text('This is Another Window!');
}
// This can be anything that fromInitData recognizes
static dynamic toInitData() => {
'class': 'OtherWindow',
};
static OtherWindowState? fromInitData(dynamic initData) {
if (initData is Map && initData['class'] == 'OtherWindow') {
return OtherWindowState();
}
return null;
}
}
Drag & Drop
It is hard to imagine any desktop user interface framework that does not support drag and drop operations. NativeShell supports drag-and-drop file paths, urls, custom Dart data (serialized by StandardMethodCodec), and can even be extended to handle custom platform-specific formats.
It should be easy to use, and I am satisfied with its results, although it does involve writing some very scary-looking code.
https://github.com/nativeshell/examples/blob/main/lib/pages/drag_drop.dart
Popup Menu
Many frameworks and applications have made such mistakes, which often surprises me. Only recently has Firefox started using local pop-up menus on macOS. No matter how sophisticated your application is, if your menu goes wrong, you will feel something wrong.
Allows you to easily create and display context menus. Considering the powerful functions of the menu system, this menu API seems simple. The menu is reactive. You can ask to rebuild the menu while Visible and NativeShell will calculate the delta and only update the menu item that has actually changed.
int _counter = 0;
void _showContextMenu(TapDownDetails e) async {
final menu = Menu(_buildContextMenu);
// Menu can be updated while visible
final timer = Timer.periodic(Duration(milliseconds: 500), (timer) {
++_counter;
// This will call the _buildContextMenu() function, diff the old
// and new menu items and only update those platform menu items that
// actually changed
menu.update();
});
await Window.of(context).showPopupMenu(menu, e.globalPosition);
timer.cancel();
}
List<MenuItem> _buildContextMenu() => [
MenuItem(title: 'Context menu Item', action: () {}),
MenuItem(title: 'Menu Update Counter $_counter', action: null),
MenuItem.separator(),
MenuItem.children(title: 'Submenu', children: [
MenuItem(title: 'Submenu Item 1', action: () {}),
MenuItem(title: 'Submenu Item 2', action: () {}),
]),
];
MenuBar
Probably my favorite feature in NativeShell. On macOS, it renders as an empty widget, but instead places the menu on the system menu bar (at the top of the screen). On Windows and Linux, it uses Flutter widgets to present top-level menu items, and then uses native menus to handle the rest. This means that the menu bar can be located anywhere in the widget hierarchy, it is not limited to the top of the window, nor does it rely on GDI or Gtk to draw iself.
It supports mouse tracking and keyboard navigation, just like a normal system menu bar, but without any restrictions.
What's the situation now
NativeShell is being heavily developed. Things are likely to break. More documentation and examples are urgently needed. But I think its shape may be useful to some people.
All three supported platforms (macOS, Windows, Linux) have completely equivalent functions.
https://github.com/nativeshell/nativeshell/tree/main/nativeshell/src/shell/platform/macos
https://github.com/nativeshell/nativeshell/tree/main/nativeshell/src/shell/platform/win32
https://github.com/nativeshell/nativeshell/tree/main/nativeshell/src/shell/platform/linux
If you get here all the way, you can continue nativeshell.dev.
Thanks for your feedback!
https://github.com/nativeshell/nativeshell/issues
© Cat brother
Past
Open source
GetX Quick Start
https://github.com/ducafecat/getx_quick_start
News client
https://github.com/ducafecat/flutter_learn_news
strapi manual translation
WeChat discussion group ducafecat
Series collection
Translation
https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/
Open source project
https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/
Dart programming language basics
https://space.bilibili.com/404904528/channel/detail?cid=111585
Getting started with Flutter zero foundation
https://space.bilibili.com/404904528/channel/detail?cid=123470
Flutter actual combat from scratch news client
https://space.bilibili.com/404904528/channel/detail?cid=106755
Flutter component development
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。