Skip to content

Commit

Permalink
flet build command to package Flet app for any platform (#2271)
Browse files Browse the repository at this point in the history
* Added `flet build` command prototype

* Bump `http` dependency

* Add `platform`, `python_app_path` and `--output_dir` to flet build command

* flet build - 1st prototype

* Build options per platform

* configurable output folder

* Copy assets

* Generate app icons and splashes

* Generate python package

* build find-links, install micropip

* Disable splash for specific platform

* --base-url, --web-renderer, --use-color-emoji, --route-url-strategy args

* Update logo-inkscape.svg

* Fix shake detector dependency

Close #2257

* Copy build directory and exclude it from a build

* More options, detect arch, copy build output

* Fix running flutter and dart on Windows

* Generate splash screen for selected platforms only

* Display output dir

* Added --team and --flutter-build-args arguments

* build number/version and template args

* Copy "assets" to web root

* Renamed platform to package

* Take stem of module_name

* package -> target_platform

* Simplified logic for Text.style and Text.theme_style

* Fix test: Text.style should support both enum and string
  • Loading branch information
FeodorFitsner authored Dec 30, 2023
1 parent 6c4e475 commit 9f97fdd
Show file tree
Hide file tree
Showing 8 changed files with 927 additions and 83 deletions.
32 changes: 12 additions & 20 deletions client/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
markdown:
dependency: transitive
description:
Expand Down Expand Up @@ -474,34 +482,18 @@ packages:
dependency: transitive
description:
name: sensors_plus
sha256: "362c8f4f001838b90dd5206b898bbad941bc0142479eab9a3415f0f79e622908"
sha256: "8e7fa79b4940442bb595bfc0ee9da4af5a22a0fe6ebacc74998245ee9496a82d"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "4.0.2"
sensors_plus_platform_interface:
dependency: transitive
description:
name: sensors_plus_platform_interface
sha256: "95f0cc08791b8bf0c41c5fa99c84be2a7d5bf60a811ddc17e1438b1e68caf0d3"
sha256: bc472d6cfd622acb4f020e726433ee31788b038056691ba433fec80e448a094f
url: "https://pub.dev"
source: hosted
version: "1.1.3"
sensors_plus_web:
dependency: transitive
description:
name: sensors_plus_web
sha256: fca8d7d9ab6233b2a059952666415508e252420be1ef54f092d07884da53ec5e
url: "https://pub.dev"
source: hosted
version: "1.1.2"
shake:
dependency: transitive
description:
name: shake
sha256: "107546951c6b8f5e4c2dca66dfb3aa27dd1a853b4e9a26c9aea224b167045023"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "1.2.0"
shared_preferences:
dependency: transitive
description:
Expand Down
76 changes: 75 additions & 1 deletion media/logo/logo-inkscape.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 121 additions & 1 deletion package/lib/src/controls/shake_detector.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:async';
import 'dart:math';

import 'package:flutter/widgets.dart';
import 'package:shake/shake.dart';
import 'package:sensors_plus/sensors_plus.dart';

import '../flet_app_services.dart';
import '../models/control.dart';
Expand Down Expand Up @@ -70,3 +73,120 @@ class _ShakeDetectorControlState extends State<ShakeDetectorControl> {
return widget.nextChild ?? const SizedBox.shrink();
}
}

/*
Source: https://github.com/dieringe/shake/blob/master/lib/shake.dart
Copyright 2019 Deven Joshi
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/// Callback for phone shakes
typedef PhoneShakeCallback = void Function();

/// ShakeDetector class for phone shake functionality
class ShakeDetector {
/// User callback for phone shake
final PhoneShakeCallback onPhoneShake;

/// Shake detection threshold
final double shakeThresholdGravity;

/// Minimum time between shake
final int shakeSlopTimeMS;

/// Time before shake count resets
final int shakeCountResetTime;

/// Number of shakes required before shake is triggered
final int minimumShakeCount;

int mShakeTimestamp = DateTime.now().millisecondsSinceEpoch;
int mShakeCount = 0;

/// StreamSubscription for Accelerometer events
StreamSubscription? streamSubscription;

/// This constructor waits until [startListening] is called
ShakeDetector.waitForStart({
required this.onPhoneShake,
this.shakeThresholdGravity = 2.7,
this.shakeSlopTimeMS = 500,
this.shakeCountResetTime = 3000,
this.minimumShakeCount = 1,
});

/// This constructor automatically calls [startListening] and starts detection and callbacks.
ShakeDetector.autoStart({
required this.onPhoneShake,
this.shakeThresholdGravity = 2.7,
this.shakeSlopTimeMS = 500,
this.shakeCountResetTime = 3000,
this.minimumShakeCount = 1,
}) {
startListening();
}

/// Starts listening to accelerometer events
void startListening() {
streamSubscription = accelerometerEvents.listen(
(AccelerometerEvent event) {
double x = event.x;
double y = event.y;
double z = event.z;

double gX = x / 9.80665;
double gY = y / 9.80665;
double gZ = z / 9.80665;

// gForce will be close to 1 when there is no movement.
double gForce = sqrt(gX * gX + gY * gY + gZ * gZ);

if (gForce > shakeThresholdGravity) {
var now = DateTime.now().millisecondsSinceEpoch;
// ignore shake events too close to each other (500ms)
if (mShakeTimestamp + shakeSlopTimeMS > now) {
return;
}

// reset the shake count after 3 seconds of no shakes
if (mShakeTimestamp + shakeCountResetTime < now) {
mShakeCount = 0;
}

mShakeTimestamp = now;
mShakeCount++;

if (mShakeCount >= minimumShakeCount) {
onPhoneShake();
}
}
},
);
}

/// Stops listening to accelerometer events
void stopListening() {
streamSubscription?.cancel();
}
}
2 changes: 1 addition & 1 deletion package/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies:
flutter_svg: ^2.0.9
window_to_front: ^0.0.3
audioplayers: ^5.2.1
shake: ^2.2.0
sensors_plus: ^4.0.2
path: ^1.8.2
js: ^0.6.5
fl_chart: ^0.65.0
Expand Down
51 changes: 11 additions & 40 deletions sdk/python/packages/flet-core/src/flet_core/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,6 @@ class TextOverflow(Enum):
VISIBLE = "visible"


TextThemeStyleString = Literal[
"displayLarge",
"displayMedium",
"displaySmall",
"headlineLarge",
"headlineMedium",
"headlineSmall",
"titleLarge",
"titleMedium",
"titleSmall",
"labelLarge",
"labelMedium",
"labelSmall",
"bodyLarge",
"bodyMedium",
"bodySmall",
]


class TextThemeStyle(Enum):
DISPLAY_LARGE = "displayLarge"
DISPLAY_MEDIUM = "displayMedium"
Expand Down Expand Up @@ -203,7 +184,7 @@ def _get_children(self):
children = []
children.extend(self.__spans)
return children

def _before_build_command(self):
super()._before_build_command()
if dataclasses.is_dataclass(self.__style):
Expand Down Expand Up @@ -279,35 +260,25 @@ def __set_weight(self, value: FontWeightString):

# style
@property
def style(self) -> Optional[Union[TextThemeStyle, TextStyle, TextThemeStyleString]]:
def style(self) -> Optional[Union[TextThemeStyle, TextStyle]]:
return self.__style

@style.setter
def style(self, value: Optional[Union[TextThemeStyle, TextStyle, TextThemeStyleString]]):
def style(self, value: Optional[Union[TextThemeStyle, TextStyle]]):
self.__style = value
if isinstance(value, TextThemeStyle):
self._set_attr("style", value.value)
else:
self.__set_style(value)

def __set_style(self, value: Optional[Union[TextStyle, TextThemeStyleString]]):
self._set_attr("style", value)
if isinstance(value, (TextThemeStyle, str)) or value is None:
self._set_attr(
"style", value.value if isinstance(value, TextThemeStyle) else value
)

# theme_style
@property
def theme_style(self) -> Optional[Union[TextThemeStyle, TextThemeStyleString]]:
return self.__theme_style
def theme_style(self):
return self._get_attr("theme_style")

@theme_style.setter
def theme_style(self, value: Optional[Union[TextThemeStyle, TextThemeStyleString]]):
self.__theme_style = value
if isinstance(value, TextThemeStyle):
self._set_attr("theme_style", value.value)
else:
self.__set_theme_style(value)

def __set_theme_style(self, value: Optional[TextThemeStyleString]):
self._set_attr("theme_style", value)
def theme_style(self, value: Optional[TextThemeStyle]):
self._set_attr("theme_style", value.value if value is not None else None)

# italic
@property
Expand Down
22 changes: 2 additions & 20 deletions sdk/python/packages/flet-runtime/src/flet_runtime/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,5 @@ def patch_manifest_json(
f.write(json.dumps(manifest, indent=2))


def copy_tree(src, dst):
"""Recursively copy a directory tree using shutil.copy2().
Arguments:
src -- source directory path
dst -- destination directory path
Return a list of files that were copied or might have been copied.
"""
if not os.path.isdir(src):
raise OSError("Source is not a directory")

os.makedirs(dst, exist_ok=True)
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isdir(s):
copy_tree(s, d)
else:
shutil.copy2(s, d)
def copy_tree(src, dst, ignore=None):
return shutil.copytree(src, dst, ignore=ignore, symlinks=True, dirs_exist_ok=True)
Loading

0 comments on commit 9f97fdd

Please sign in to comment.