Skip to content

Commit ec7084a

Browse files
First Commit
0 parents  commit ec7084a

40 files changed

+1890
-0
lines changed

.gitignore

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
**/ios/Flutter/.last_build_id
26+
.dart_tool/
27+
.flutter-plugins
28+
.flutter-plugins-dependencies
29+
.packages
30+
.pub-cache/
31+
.pub/
32+
/build/
33+
34+
# Web related
35+
lib/generated_plugin_registrant.dart
36+
37+
# Symbolication related
38+
app.*.symbols
39+
40+
# Obfuscation related
41+
app.*.map.json
42+
43+
# Android Studio will place build artifacts here
44+
/android/app/debug
45+
/android/app/profile
46+
/android/app/release

.metadata

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 7e9793dee1b85a243edd0e06cb1658e98b077561
8+
channel: stable
9+
10+
project_type: app

Demo.png

739 KB
Loading

README.md

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# flutter_macos_menubar_example
2+
3+
starter template for building menubar app in flutter
4+
![Demo](Demo.png)
5+
6+
## Getting Started
7+
8+
1. Change to Content of macos/Runner/AppDelegate.swift
9+
```[language=swift]
10+
var statusBar: StatusBarController?
11+
var popover = NSPopover.init()
12+
override init() {
13+
popover.behavior = NSPopover.Behavior.transient //to make the popover hide when the user clicks outside of it
14+
}
15+
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
16+
return false
17+
}
18+
override func applicationDidFinishLaunching(_ aNotification: Notification) {
19+
let controller: FlutterViewController =
20+
mainFlutterWindow?.contentViewController as! FlutterViewController
21+
popover.contentSize = NSSize(width: 360, height: 360) //change this to your desired size
22+
popover.contentViewController = controller //set the content view controller for the popover to flutter view controller
23+
statusBar = StatusBarController.init(popover)
24+
mainFlutterWindow.close() //close the default flutter window
25+
super.applicationDidFinishLaunching(aNotification)
26+
}
27+
```
28+
2. Add a new Swift file named 'StatusBarController.swift' in XCode
29+
![Xcode > File > New > File...](Step2.1.png)
30+
![Select Swift File](Step2.2.png)
31+
![Name it StatusBarController.swift](Step2.3.png)
32+
3. Add the following code to the StatusBarController.swift
33+
```[language=swift]
34+
import AppKit
35+
36+
class StatusBarController {
37+
private var statusBar: NSStatusBar
38+
private var statusItem: NSStatusItem
39+
private var popover: NSPopover
40+
41+
init(_ popover: NSPopover) {
42+
self.popover = popover
43+
statusBar = NSStatusBar.init()
44+
statusItem = statusBar.statusItem(withLength: 28.0)
45+
46+
if let statusBarButton = statusItem.button {
47+
statusBarButton.image = #imageLiteral(resourceName: "AppIcon") //change this to your desired image
48+
statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0)
49+
statusBarButton.image?.isTemplate = true
50+
statusBarButton.action = #selector(togglePopover(sender:))
51+
statusBarButton.target = self
52+
}
53+
}
54+
55+
@objc func togglePopover(sender: AnyObject) {
56+
if(popover.isShown) {
57+
hidePopover(sender)
58+
}
59+
else {
60+
showPopover(sender)
61+
}
62+
}
63+
64+
func showPopover(_ sender: AnyObject) {
65+
if let statusBarButton = statusItem.button {
66+
popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY)
67+
}
68+
}
69+
70+
func hidePopover(_ sender: AnyObject) {
71+
popover.performClose(sender)
72+
}
73+
}
74+
```
75+
4. Add the following to macos/Runner/Info.plist to hide the dock icon and application menu
76+
```[language=xml]
77+
<key>LSUIElement</key>
78+
<true/>
79+
```
80+
To close the app programmatically, use platform channel to call the following code
81+
```[language=dart]
82+
exit(0)
83+
```
84+
5. Done!
85+
You can now build your menubar app with flutter.
86+
![Complete](Demo.png)
87+
-------------------------------------------------------
88+
89+
This project is a starting point for a Flutter menubar application in macOS.
90+
91+
A few resources to get you started if this is your first Flutter project:
92+
93+
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
94+
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
95+
96+
For help getting started with Flutter, view our
97+
[online documentation](https://flutter.dev/docs), which offers tutorials,
98+
samples, guidance on mobile development, and a full API reference.

Step2.1.png

671 KB
Loading

Step2.2.png

123 KB
Loading

Step2.3.png

218 KB
Loading

analysis_options.yaml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# This file configures the analyzer, which statically analyzes Dart code to
2+
# check for errors, warnings, and lints.
3+
#
4+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6+
# invoked from the command line by running `flutter analyze`.
7+
8+
# The following line activates a set of recommended lints for Flutter apps,
9+
# packages, and plugins designed to encourage good coding practices.
10+
include: package:flutter_lints/flutter.yaml
11+
12+
linter:
13+
# The lint rules applied to this project can be customized in the
14+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
15+
# included above or to enable additional rules. A list of all available lints
16+
# and their documentation is published at
17+
# https://dart-lang.github.io/linter/lints/index.html.
18+
#
19+
# Instead of disabling a lint rule for the entire project in the
20+
# section below, it can also be suppressed for a single line of code
21+
# or a specific dart file by using the `// ignore: name_of_lint` and
22+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
23+
# producing the lint.
24+
rules:
25+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
26+
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27+
28+
# Additional information about this file can be found at
29+
# https://dart.dev/guides/language/analysis-options

lib/main.dart

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart';
4+
5+
void main() {
6+
runApp(const MyApp());
7+
}
8+
9+
class MyApp extends StatelessWidget {
10+
const MyApp({Key? key}) : super(key: key);
11+
12+
// This widget is the root of your application.
13+
@override
14+
Widget build(BuildContext context) {
15+
return MaterialApp(
16+
title: 'Flutter Demo',
17+
theme: ThemeData(
18+
// This is the theme of your application.
19+
//
20+
// Try running your application with "flutter run". You'll see the
21+
// application has a blue toolbar. Then, without quitting the app, try
22+
// changing the primarySwatch below to Colors.green and then invoke
23+
// "hot reload" (press "r" in the console where you ran "flutter run",
24+
// or simply save your changes to "hot reload" in a Flutter IDE).
25+
// Notice that the counter didn't reset back to zero; the application
26+
// is not restarted.
27+
primarySwatch: Colors.blue,
28+
),
29+
home: const MyHomePage(title: 'Flutter Demo Home Page'),
30+
);
31+
}
32+
}
33+
34+
class MyHomePage extends StatefulWidget {
35+
const MyHomePage({Key? key, required this.title}) : super(key: key);
36+
37+
// This widget is the home page of your application. It is stateful, meaning
38+
// that it has a State object (defined below) that contains fields that affect
39+
// how it looks.
40+
41+
// This class is the configuration for the state. It holds the values (in this
42+
// case the title) provided by the parent (in this case the App widget) and
43+
// used by the build method of the State. Fields in a Widget subclass are
44+
// always marked "final".
45+
46+
final String title;
47+
48+
@override
49+
State<MyHomePage> createState() => _MyHomePageState();
50+
}
51+
52+
class _MyHomePageState extends State<MyHomePage> {
53+
int _counter = 0;
54+
55+
void _incrementCounter() {
56+
setState(() {
57+
// This call to setState tells the Flutter framework that something has
58+
// changed in this State, which causes it to rerun the build method below
59+
// so that the display can reflect the updated values. If we changed
60+
// _counter without calling setState(), then the build method would not be
61+
// called again, and so nothing would appear to happen.
62+
_counter++;
63+
});
64+
}
65+
66+
@override
67+
Widget build(BuildContext context) {
68+
// This method is rerun every time setState is called, for instance as done
69+
// by the _incrementCounter method above.
70+
//
71+
// The Flutter framework has been optimized to make rerunning build methods
72+
// fast, so that you can just rebuild anything that needs updating rather
73+
// than having to individually change instances of widgets.
74+
return Scaffold(
75+
appBar: AppBar(
76+
// Here we take the value from the MyHomePage object that was created by
77+
// the App.build method, and use it to set our appbar title.
78+
title: Text(widget.title),
79+
),
80+
body: Center(
81+
// Center is a layout widget. It takes a single child and positions it
82+
// in the middle of the parent.
83+
child: Column(
84+
// Column is also a layout widget. It takes a list of children and
85+
// arranges them vertically. By default, it sizes itself to fit its
86+
// children horizontally, and tries to be as tall as its parent.
87+
//
88+
// Invoke "debug painting" (press "p" in the console, choose the
89+
// "Toggle Debug Paint" action from the Flutter Inspector in Android
90+
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
91+
// to see the wireframe for each widget.
92+
//
93+
// Column has various properties to control how it sizes itself and
94+
// how it positions its children. Here we use mainAxisAlignment to
95+
// center the children vertically; the main axis here is the vertical
96+
// axis because Columns are vertical (the cross axis would be
97+
// horizontal).
98+
mainAxisAlignment: MainAxisAlignment.center,
99+
children: <Widget>[
100+
const Text(
101+
'You have pushed the button this many times:',
102+
),
103+
Text(
104+
'$_counter',
105+
style: Theme.of(context).textTheme.headline4,
106+
),
107+
MaterialButton(
108+
onPressed: () {
109+
exit(0);
110+
},
111+
child: Text('Close App')),
112+
],
113+
),
114+
),
115+
floatingActionButton: FloatingActionButton(
116+
onPressed: _incrementCounter,
117+
tooltip: 'Increment',
118+
child: const Icon(Icons.add),
119+
), // This trailing comma makes auto-formatting nicer for build methods.
120+
);
121+
}
122+
}

macos/.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Flutter-related
2+
**/Flutter/ephemeral/
3+
**/Pods/
4+
5+
# Xcode-related
6+
**/dgph
7+
**/xcuserdata/

macos/Flutter/Flutter-Debug.xcconfig

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "ephemeral/Flutter-Generated.xcconfig"
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "ephemeral/Flutter-Generated.xcconfig"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// Generated file. Do not edit.
3+
//
4+
5+
import FlutterMacOS
6+
import Foundation
7+
8+
9+
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
10+
}

0 commit comments

Comments
 (0)