Upgrade Flutter
This commit is contained in:
parent
a9393134b8
commit
ca278c046a
12
.metadata
12
.metadata
|
|
@ -4,7 +4,7 @@
|
||||||
# This file should be version controlled.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
@ -13,11 +13,11 @@ project_type: app
|
||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||||
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||||
- platform: macos
|
- platform: linux
|
||||||
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||||
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,13 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authenticated = await LocalAuthentication().authenticate(
|
authenticated = await LocalAuthentication().authenticate(
|
||||||
localizedReason: 'Please authenticate to access the app',
|
localizedReason: 'Please authenticate to access the app',
|
||||||
|
options: const AuthenticationOptions(
|
||||||
biometricOnly: true,
|
biometricOnly: true,
|
||||||
useErrorDialogs: true,
|
useErrorDialogs: true,
|
||||||
stickyAuth: false);
|
stickyAuth: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +387,11 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
? LockScreen(onAuthenticatePressed: _authenticate)
|
? LockScreen(onAuthenticatePressed: _authenticate)
|
||||||
: InitScreen(),
|
: InitScreen(),
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
/*
|
||||||
|
theme: state.prefState.enableDarkMode
|
||||||
|
? ThemeData.dark(useMaterial3: true)
|
||||||
|
: ThemeData.light(useMaterial3: true),
|
||||||
|
*/
|
||||||
theme: state.prefState.enableDarkMode
|
theme: state.prefState.enableDarkMode
|
||||||
? ThemeData(
|
? ThemeData(
|
||||||
colorScheme: ColorScheme.dark().copyWith(
|
colorScheme: ColorScheme.dark().copyWith(
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:image_cropper/image_cropper.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:share/share.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
|
|
@ -227,7 +227,7 @@ class DocumentTile extends StatelessWidget {
|
||||||
|
|
||||||
await File(filePath)
|
await File(filePath)
|
||||||
.writeAsBytes(response.bodyBytes);
|
.writeAsBytes(response.bodyBytes);
|
||||||
await Share.shareFiles([filePath]);
|
await Share.shareXFiles([XFile(filePath)]);
|
||||||
}
|
}
|
||||||
} else if (value == localization.delete) {
|
} else if (value == localization.delete) {
|
||||||
confirmCallback(
|
confirmCallback(
|
||||||
|
|
|
||||||
|
|
@ -1400,7 +1400,7 @@ void _showAbout(BuildContext context) async {
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: AppButton(
|
child: AppButton(
|
||||||
label: localization.appPlatforms.toUpperCase(),
|
label: localization.appPlatforms.toUpperCase(),
|
||||||
iconData: MdiIcons.desktopMac,
|
iconData: MdiIcons.desktopClassic,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog<AlertDialog>(
|
showDialog<AlertDialog>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
import 'package:share/share.dart';
|
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
import 'package:invoiceninja_flutter/data/models/dashboard_model.dart';
|
import 'package:invoiceninja_flutter/data/models/dashboard_model.dart';
|
||||||
|
|
@ -40,6 +39,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||||
|
|
||||||
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
||||||
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
|
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
class ClientPdfView extends StatefulWidget {
|
class ClientPdfView extends StatefulWidget {
|
||||||
const ClientPdfView({
|
const ClientPdfView({
|
||||||
|
|
@ -404,7 +404,7 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
||||||
showToast(
|
showToast(
|
||||||
localization.fileSavedInDownloadsFolder);
|
localization.fileSavedInDownloadsFolder);
|
||||||
} else {
|
} else {
|
||||||
await Share.shareFiles([filePath]);
|
await Share.shareXFiles([XFile(filePath)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -439,37 +439,39 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
||||||
callback: (_) => loadPDF(sendEmail: true));
|
callback: (_) => loadPDF(sendEmail: true));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextButton(
|
if (supportsSchedules())
|
||||||
onPressed: () {
|
TextButton(
|
||||||
if (!state.isProPlan) {
|
onPressed: () {
|
||||||
showMessageDialog(
|
if (!state.isProPlan) {
|
||||||
context: context,
|
showMessageDialog(
|
||||||
message: localization.upgradeToPaidPlanToSchedule,
|
context: context,
|
||||||
secondaryActions: [
|
message: localization.upgradeToPaidPlanToSchedule,
|
||||||
TextButton(
|
secondaryActions: [
|
||||||
onPressed: () {
|
TextButton(
|
||||||
store.dispatch(ViewSettings(
|
onPressed: () {
|
||||||
section: kSettingsAccountManagement));
|
store.dispatch(ViewSettings(
|
||||||
Navigator.of(context).pop();
|
section: kSettingsAccountManagement));
|
||||||
},
|
Navigator.of(context).pop();
|
||||||
child:
|
},
|
||||||
Text(localization.upgrade.toUpperCase())),
|
child: Text(
|
||||||
]);
|
localization.upgrade.toUpperCase())),
|
||||||
return;
|
]);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
createEntity(
|
createEntity(
|
||||||
context: context,
|
context: context,
|
||||||
entity: ScheduleEntity().rebuild((b) => b
|
entity: ScheduleEntity().rebuild((b) => b
|
||||||
..template = ScheduleEntity.TEMPLATE_EMAIL_STATEMENT
|
..template =
|
||||||
..parameters.clients.add(client.id)
|
ScheduleEntity.TEMPLATE_EMAIL_STATEMENT
|
||||||
..parameters.showAgingTable = _showAging
|
..parameters.clients.add(client.id)
|
||||||
..parameters.showPaymentsTable = _showPayments
|
..parameters.showAgingTable = _showAging
|
||||||
..parameters.status = _status
|
..parameters.showPaymentsTable = _showPayments
|
||||||
..parameters.dateRange = _dateRange.snakeCase));
|
..parameters.status = _status
|
||||||
},
|
..parameters.dateRange = _dateRange.snakeCase));
|
||||||
child: Text(localization.schedule,
|
},
|
||||||
style: TextStyle(color: state.headerTextColor))),
|
child: Text(localization.schedule,
|
||||||
|
style: TextStyle(color: state.headerTextColor))),
|
||||||
if (isDesktop(context))
|
if (isDesktop(context))
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(localization.close,
|
child: Text(localization.close,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import 'package:invoiceninja_flutter/main_app.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
import 'package:share/share.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
|
|
@ -282,7 +282,7 @@ class _InvoicePdfViewState extends State<InvoicePdfView> {
|
||||||
showToast(localization
|
showToast(localization
|
||||||
.fileSavedInDownloadsFolder);
|
.fileSavedInDownloadsFolder);
|
||||||
} else {
|
} else {
|
||||||
await Share.shareFiles([filePath]);
|
await Share.shareXFiles([XFile(filePath)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||||
import 'package:memoize/memoize.dart';
|
import 'package:memoize/memoize.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
import 'package:share/share.dart';
|
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
import 'package:invoiceninja_flutter/constants.dart';
|
import 'package:invoiceninja_flutter/constants.dart';
|
||||||
|
|
@ -51,6 +50,7 @@ import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/money.dart';
|
import 'package:invoiceninja_flutter/utils/money.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/strings.dart';
|
import 'package:invoiceninja_flutter/utils/strings.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'credit_report.dart';
|
import 'credit_report.dart';
|
||||||
|
|
||||||
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
||||||
|
|
@ -515,7 +515,7 @@ class ReportsScreenVM {
|
||||||
if (isDesktopOS()) {
|
if (isDesktopOS()) {
|
||||||
showToast(localization.fileSavedInDownloadsFolder);
|
showToast(localization.fileSavedInDownloadsFolder);
|
||||||
} else {
|
} else {
|
||||||
await Share.shareFiles([filePath]);
|
await Share.shareXFiles([XFile(filePath)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,10 @@ class DeviceSettingsVM {
|
||||||
authenticated = await LocalAuthentication().authenticate(
|
authenticated = await LocalAuthentication().authenticate(
|
||||||
localizedReason:
|
localizedReason:
|
||||||
AppLocalization.of(context).authenticateToChangeSetting,
|
AppLocalization.of(context).authenticateToChangeSetting,
|
||||||
biometricOnly: true,
|
options: const AuthenticationOptions(
|
||||||
useErrorDialogs: true,
|
biometricOnly: true,
|
||||||
stickyAuth: false);
|
useErrorDialogs: true,
|
||||||
|
stickyAuth: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,10 +240,11 @@ class _SettingsListState extends State<SettingsList> {
|
||||||
),
|
),
|
||||||
*/
|
*/
|
||||||
if (showAll) ...[
|
if (showAll) ...[
|
||||||
SettingsListTile(
|
if (supportsSchedules())
|
||||||
section: kSettingsSchedules,
|
SettingsListTile(
|
||||||
viewModel: widget.viewModel,
|
section: kSettingsSchedules,
|
||||||
),
|
viewModel: widget.viewModel,
|
||||||
|
),
|
||||||
SettingsListTile(
|
SettingsListTile(
|
||||||
section: kSettingsUserManagement,
|
section: kSettingsUserManagement,
|
||||||
viewModel: widget.viewModel,
|
viewModel: widget.viewModel,
|
||||||
|
|
@ -623,11 +624,12 @@ class SettingsSearch extends StatelessWidget {
|
||||||
'subscriptions',
|
'subscriptions',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
kSettingsSchedules: [
|
if (supportsSchedules())
|
||||||
[
|
kSettingsSchedules: [
|
||||||
'schedules#2023-02-15',
|
[
|
||||||
|
'schedules#2023-02-15',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
|
||||||
kSettingsUserManagement: [
|
kSettingsUserManagement: [
|
||||||
[
|
[
|
||||||
'users',
|
'users',
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ bool supportsAppleOAuth() => kIsWeb || isApple();
|
||||||
// TODO remove this function
|
// TODO remove this function
|
||||||
bool supportsMicrosoftOAuth() => kIsWeb;
|
bool supportsMicrosoftOAuth() => kIsWeb;
|
||||||
|
|
||||||
|
// TODO remove this
|
||||||
|
bool supportsSchedules() {
|
||||||
|
final store = StoreProvider.of<AppState>(navigatorKey.currentContext);
|
||||||
|
return store.state.isSelfHosted;
|
||||||
|
}
|
||||||
|
|
||||||
bool supportsInAppPurchase() {
|
bool supportsInAppPurchase() {
|
||||||
final store = StoreProvider.of<AppState>(navigatorKey.currentContext);
|
final store = StoreProvider.of<AppState>(navigatorKey.currentContext);
|
||||||
if (store.state.isSelfHosted) {
|
if (store.state.isSelfHosted) {
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,20 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
|
|
||||||
ScrollController _scrollController;
|
ScrollController _scrollController;
|
||||||
|
|
||||||
|
final _darkBackground = const Color(0xFF222222);
|
||||||
|
final _lightBackground = Colors.white;
|
||||||
|
Brightness _brightness = Brightness.light;
|
||||||
|
|
||||||
|
SuperEditorDebugVisualsConfig _debugConfig;
|
||||||
|
|
||||||
OverlayEntry _textFormatBarOverlayEntry;
|
OverlayEntry _textFormatBarOverlayEntry;
|
||||||
final _textSelectionAnchor = ValueNotifier<Offset>(null);
|
final _textSelectionAnchor = ValueNotifier<Offset>(null);
|
||||||
|
|
||||||
OverlayEntry _imageFormatBarOverlayEntry;
|
OverlayEntry _imageFormatBarOverlayEntry;
|
||||||
final _imageSelectionAnchor = ValueNotifier<Offset>(null);
|
final _imageSelectionAnchor = ValueNotifier<Offset>(null);
|
||||||
|
|
||||||
|
final _overlayController = MagnifierAndToolbarController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -55,8 +63,10 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
_doc = deserializeMarkdownToDocument(markdown)
|
_doc = deserializeMarkdownToDocument(markdown)
|
||||||
..addListener(_hideOrShowToolbar)
|
..addListener(_hideOrShowToolbar)
|
||||||
..addListener(_onChanged);
|
..addListener(_onChanged);
|
||||||
|
|
||||||
_docEditor = DocumentEditor(document: _doc as MutableDocument);
|
_docEditor = DocumentEditor(document: _doc as MutableDocument);
|
||||||
_composer = DocumentComposer()..addListener(_hideOrShowToolbar);
|
_composer = DocumentComposer();
|
||||||
|
_composer.selectionNotifier.addListener(_hideOrShowToolbar);
|
||||||
_docOps = CommonEditorOperations(
|
_docOps = CommonEditorOperations(
|
||||||
editor: _docEditor,
|
editor: _docEditor,
|
||||||
composer: _composer,
|
composer: _composer,
|
||||||
|
|
@ -169,15 +179,16 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
|
|
||||||
void _showEditorToolbar() {
|
void _showEditorToolbar() {
|
||||||
if (_textFormatBarOverlayEntry == null) {
|
if (_textFormatBarOverlayEntry == null) {
|
||||||
|
// Create an overlay entry to build the editor toolbar.
|
||||||
|
// TODO: add an overlay to the Editor widget to avoid using the
|
||||||
|
// application overlay
|
||||||
_textFormatBarOverlayEntry ??= OverlayEntry(builder: (context) {
|
_textFormatBarOverlayEntry ??= OverlayEntry(builder: (context) {
|
||||||
return Theme(
|
return EditorToolbar(
|
||||||
data: ThemeData.light(),
|
anchor: _textSelectionAnchor,
|
||||||
child: EditorToolbar(
|
editorFocusNode: _editorFocusNode,
|
||||||
anchor: _textSelectionAnchor,
|
editor: _docEditor,
|
||||||
editor: _docEditor,
|
composer: _composer,
|
||||||
composer: _composer,
|
closeToolbar: _hideEditorToolbar,
|
||||||
closeToolbar: _hideEditorToolbar,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -199,7 +210,6 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
_composer.selection.base, _composer.selection.extent);
|
_composer.selection.base, _composer.selection.extent);
|
||||||
final docBox =
|
final docBox =
|
||||||
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
final overlayBoundingBox = Rect.fromPoints(
|
final overlayBoundingBox = Rect.fromPoints(
|
||||||
docBox.localToGlobal(docBoundingBox.topLeft),
|
docBox.localToGlobal(docBoundingBox.topLeft),
|
||||||
docBox.localToGlobal(docBoundingBox.bottomRight),
|
docBox.localToGlobal(docBoundingBox.bottomRight),
|
||||||
|
|
@ -222,14 +232,14 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
// and non-null implies the entry is in the overlay.
|
// and non-null implies the entry is in the overlay.
|
||||||
_textFormatBarOverlayEntry.remove();
|
_textFormatBarOverlayEntry.remove();
|
||||||
_textFormatBarOverlayEntry = null;
|
_textFormatBarOverlayEntry = null;
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that focus returns to the editor.
|
// Ensure that focus returns to the editor.
|
||||||
//
|
//
|
||||||
// I tried explicitly unfocus()'ing the URL textfield
|
// I tried explicitly unfocus()'ing the URL textfield
|
||||||
// in the toolbar but it didn't return focus to the
|
// in the toolbar but it didn't return focus to the
|
||||||
// editor. I'm not sure why.
|
// editor. I'm not sure why.
|
||||||
_editorFocusNode.requestFocus();
|
_editorFocusNode.requestFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentGestureMode get _gestureMode {
|
DocumentGestureMode get _gestureMode {
|
||||||
|
|
@ -245,29 +255,41 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
return DocumentGestureMode.mouse;
|
return DocumentGestureMode.mouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return DocumentGestureMode.mouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isMobile => _gestureMode != DocumentGestureMode.mouse;
|
bool get _isMobile => _gestureMode != DocumentGestureMode.mouse;
|
||||||
|
|
||||||
DocumentInputSource get _inputSource {
|
TextInputSource get _inputSource {
|
||||||
switch (defaultTargetPlatform) {
|
switch (defaultTargetPlatform) {
|
||||||
case TargetPlatform.android:
|
case TargetPlatform.android:
|
||||||
case TargetPlatform.iOS:
|
case TargetPlatform.iOS:
|
||||||
return DocumentInputSource.ime;
|
|
||||||
case TargetPlatform.fuchsia:
|
case TargetPlatform.fuchsia:
|
||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
case TargetPlatform.windows:
|
case TargetPlatform.windows:
|
||||||
return DocumentInputSource.keyboard;
|
return TextInputSource.ime;
|
||||||
|
// return DocumentInputSource.keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return TextInputSource.ime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cut() {
|
||||||
|
_docOps.cut();
|
||||||
|
_overlayController.hideToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copy() {
|
||||||
|
_docOps.copy();
|
||||||
|
_overlayController.hideToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _paste() {
|
||||||
|
_docOps.paste();
|
||||||
|
_overlayController.hideToolbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cut() => _docOps.cut();
|
|
||||||
void _copy() => _docOps.copy();
|
|
||||||
void _paste() => _docOps.paste();
|
|
||||||
void _selectAll() => _docOps.selectAll();
|
void _selectAll() => _docOps.selectAll();
|
||||||
|
|
||||||
void _showImageToolbar() {
|
void _showImageToolbar() {
|
||||||
|
|
@ -277,7 +299,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
return ImageFormatToolbar(
|
return ImageFormatToolbar(
|
||||||
anchor: _imageSelectionAnchor,
|
anchor: _imageSelectionAnchor,
|
||||||
composer: _composer,
|
composer: _composer,
|
||||||
setWidth: (dynamic nodeId, dynamic width) {
|
setWidth: (nodeId, width) {
|
||||||
final node = _doc.getNodeById(nodeId);
|
final node = _doc.getNodeById(nodeId);
|
||||||
final currentStyles =
|
final currentStyles =
|
||||||
SingleColumnLayoutComponentStyles.fromMetadata(node);
|
SingleColumnLayoutComponentStyles.fromMetadata(node);
|
||||||
|
|
@ -309,10 +331,8 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
final docBox =
|
final docBox =
|
||||||
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
||||||
final overlayBoundingBox = Rect.fromPoints(
|
final overlayBoundingBox = Rect.fromPoints(
|
||||||
docBox.localToGlobal(docBoundingBox.topLeft,
|
docBox.localToGlobal(docBoundingBox.topLeft),
|
||||||
ancestor: context.findRenderObject()),
|
docBox.localToGlobal(docBoundingBox.bottomRight),
|
||||||
docBox.localToGlobal(docBoundingBox.bottomRight,
|
|
||||||
ancestor: context.findRenderObject()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
_imageSelectionAnchor.value = overlayBoundingBox.center;
|
_imageSelectionAnchor.value = overlayBoundingBox.center;
|
||||||
|
|
@ -332,46 +352,152 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
// and non-null implies the entry is in the overlay.
|
// and non-null implies the entry is in the overlay.
|
||||||
_imageFormatBarOverlayEntry.remove();
|
_imageFormatBarOverlayEntry.remove();
|
||||||
_imageFormatBarOverlayEntry = null;
|
_imageFormatBarOverlayEntry = null;
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that focus returns to the editor.
|
// Ensure that focus returns to the editor.
|
||||||
_editorFocusNode.requestFocus();
|
_editorFocusNode.requestFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Theme(
|
||||||
children: [
|
data: ThemeData(brightness: _brightness),
|
||||||
Expanded(
|
child: Builder(builder: (themedContext) {
|
||||||
child: _buildEditor(),
|
// This builder captures the new theme
|
||||||
),
|
return Stack(
|
||||||
if (_isMobile) _buildMountedToolbar(),
|
children: [
|
||||||
],
|
Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildEditor(themedContext),
|
||||||
|
),
|
||||||
|
if (_isMobile) _buildMountedToolbar(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: _buildCornerFabs(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEditor() {
|
Widget _buildCornerFabs() {
|
||||||
return SuperEditor(
|
return Padding(
|
||||||
editor: _docEditor,
|
padding: const EdgeInsets.only(right: 16, bottom: 16),
|
||||||
composer: _composer,
|
child: Column(
|
||||||
focusNode: _editorFocusNode,
|
mainAxisSize: MainAxisSize.min,
|
||||||
scrollController: _scrollController,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
documentLayoutKey: _docLayoutKey,
|
children: [
|
||||||
componentBuilders: [
|
_buildDebugVisualsToggle(),
|
||||||
...defaultComponentBuilders,
|
const SizedBox(height: 16),
|
||||||
],
|
_buildLightAndDarkModeToggle(),
|
||||||
gestureMode: _gestureMode,
|
],
|
||||||
inputSource: _inputSource,
|
|
||||||
androidToolbarBuilder: (_) => AndroidTextEditingFloatingToolbar(
|
|
||||||
onCutPressed: _cut,
|
|
||||||
onCopyPressed: _copy,
|
|
||||||
onPastePressed: _paste,
|
|
||||||
onSelectAllPressed: _selectAll,
|
|
||||||
),
|
),
|
||||||
iOSToolbarBuilder: (_) => IOSTextEditingFloatingToolbar(
|
);
|
||||||
onCutPressed: _cut,
|
}
|
||||||
onCopyPressed: _copy,
|
|
||||||
onPastePressed: _paste,
|
Widget _buildDebugVisualsToggle() {
|
||||||
|
return FloatingActionButton(
|
||||||
|
backgroundColor:
|
||||||
|
_brightness == Brightness.light ? _darkBackground : _lightBackground,
|
||||||
|
foregroundColor:
|
||||||
|
_brightness == Brightness.light ? _lightBackground : _darkBackground,
|
||||||
|
elevation: 5,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_debugConfig = _debugConfig != null
|
||||||
|
? null
|
||||||
|
: SuperEditorDebugVisualsConfig(
|
||||||
|
showFocus: true,
|
||||||
|
showImeConnection: true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.bug_report,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLightAndDarkModeToggle() {
|
||||||
|
return FloatingActionButton(
|
||||||
|
backgroundColor:
|
||||||
|
_brightness == Brightness.light ? _darkBackground : _lightBackground,
|
||||||
|
foregroundColor:
|
||||||
|
_brightness == Brightness.light ? _lightBackground : _darkBackground,
|
||||||
|
elevation: 5,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_brightness = _brightness == Brightness.light
|
||||||
|
? Brightness.dark
|
||||||
|
: Brightness.light;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: _brightness == Brightness.light
|
||||||
|
? const Icon(
|
||||||
|
Icons.dark_mode,
|
||||||
|
)
|
||||||
|
: const Icon(
|
||||||
|
Icons.light_mode,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEditor(BuildContext context) {
|
||||||
|
final isLight = Theme.of(context).brightness == Brightness.light;
|
||||||
|
|
||||||
|
return ColoredBox(
|
||||||
|
color: isLight ? _lightBackground : _darkBackground,
|
||||||
|
child: SuperEditorDebugVisuals(
|
||||||
|
config: _debugConfig ?? const SuperEditorDebugVisualsConfig(),
|
||||||
|
child: SuperEditor(
|
||||||
|
editor: _docEditor,
|
||||||
|
composer: _composer,
|
||||||
|
focusNode: _editorFocusNode,
|
||||||
|
scrollController: _scrollController,
|
||||||
|
documentLayoutKey: _docLayoutKey,
|
||||||
|
documentOverlayBuilders: [
|
||||||
|
DefaultCaretOverlayBuilder(
|
||||||
|
CaretStyle()
|
||||||
|
.copyWith(color: isLight ? Colors.black : Colors.redAccent),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
selectionStyle: isLight
|
||||||
|
? defaultSelectionStyle
|
||||||
|
: SelectionStyles(
|
||||||
|
selectionColor: Colors.red.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
stylesheet: defaultStylesheet.copyWith(
|
||||||
|
addRulesAfter: [
|
||||||
|
if (!isLight) ..._darkModeStyles,
|
||||||
|
taskStyles,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
componentBuilders: [
|
||||||
|
TaskComponentBuilder(_docEditor),
|
||||||
|
...defaultComponentBuilders,
|
||||||
|
],
|
||||||
|
gestureMode: _gestureMode,
|
||||||
|
inputSource: _inputSource,
|
||||||
|
keyboardActions: _inputSource == TextInputSource.ime
|
||||||
|
? defaultImeKeyboardActions
|
||||||
|
: defaultKeyboardActions,
|
||||||
|
androidToolbarBuilder: (_) => AndroidTextEditingFloatingToolbar(
|
||||||
|
onCutPressed: _cut,
|
||||||
|
onCopyPressed: _copy,
|
||||||
|
onPastePressed: _paste,
|
||||||
|
onSelectAllPressed: _selectAll,
|
||||||
|
),
|
||||||
|
iOSToolbarBuilder: (_) => IOSTextEditingFloatingToolbar(
|
||||||
|
onCutPressed: _cut,
|
||||||
|
onCopyPressed: _copy,
|
||||||
|
onPastePressed: _paste,
|
||||||
|
),
|
||||||
|
overlayController: _overlayController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -398,3 +524,37 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Makes text light, for use during dark mode styling.
|
||||||
|
final _darkModeStyles = [
|
||||||
|
StyleRule(
|
||||||
|
BlockSelector.all,
|
||||||
|
(doc, docNode) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'textStyle': const TextStyle(
|
||||||
|
color: Color(0xFFCCCCCC),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StyleRule(
|
||||||
|
const BlockSelector("header1"),
|
||||||
|
(doc, docNode) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'textStyle': const TextStyle(
|
||||||
|
color: Color(0xFF888888),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StyleRule(
|
||||||
|
const BlockSelector("header2"),
|
||||||
|
(doc, docNode) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'textStyle': const TextStyle(
|
||||||
|
color: Color(0xFF888888),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class EditorToolbar extends StatefulWidget {
|
||||||
const EditorToolbar({
|
const EditorToolbar({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.anchor,
|
@required this.anchor,
|
||||||
|
@required this.editorFocusNode,
|
||||||
@required this.editor,
|
@required this.editor,
|
||||||
@required this.composer,
|
@required this.composer,
|
||||||
@required this.closeToolbar,
|
@required this.closeToolbar,
|
||||||
|
|
@ -26,6 +27,9 @@ class EditorToolbar extends StatefulWidget {
|
||||||
/// reposition itself as the [Offset] value changes.
|
/// reposition itself as the [Offset] value changes.
|
||||||
final ValueNotifier<Offset> anchor;
|
final ValueNotifier<Offset> anchor;
|
||||||
|
|
||||||
|
/// The [FocusNode] attached to the editor to which this toolbar applies.
|
||||||
|
final FocusNode editorFocusNode;
|
||||||
|
|
||||||
/// The [editor] is used to alter document content, such as
|
/// The [editor] is used to alter document content, such as
|
||||||
/// when the user selects a different block format for a
|
/// when the user selects a different block format for a
|
||||||
/// text blob, e.g., paragraph, header, blockquote, or
|
/// text blob, e.g., paragraph, header, blockquote, or
|
||||||
|
|
@ -49,13 +53,13 @@ class EditorToolbar extends StatefulWidget {
|
||||||
class _EditorToolbarState extends State<EditorToolbar> {
|
class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
bool _showUrlField = false;
|
bool _showUrlField = false;
|
||||||
FocusNode _urlFocusNode;
|
FocusNode _urlFocusNode;
|
||||||
TextEditingController _urlController;
|
AttributedTextEditingController _urlController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_urlFocusNode = FocusNode();
|
_urlFocusNode = FocusNode();
|
||||||
_urlController = TextEditingController();
|
_urlController = SingleLineAttributedTextEditingController(_applyLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -112,7 +116,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
/*
|
/*
|
||||||
/// Returns the text alignment of the currently selected text node.
|
/// Returns the text alignment of the currently selected text node.
|
||||||
///
|
///
|
||||||
/// Throws an exception if the currently selected node is not a text node.
|
/// Throws an exception if the currently selected node is not a text node.
|
||||||
TextAlign _getCurrentTextAlignment() {
|
TextAlign _getCurrentTextAlignment() {
|
||||||
final selectedNode = widget.editor.document
|
final selectedNode = widget.editor.document
|
||||||
.getNodeById(widget.composer.selection.extent.nodeId);
|
.getNodeById(widget.composer.selection.extent.nodeId);
|
||||||
|
|
@ -352,7 +356,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
/// Takes the text from the [urlController] and applies it as a link
|
/// Takes the text from the [urlController] and applies it as a link
|
||||||
/// attribution to the currently selected text.
|
/// attribution to the currently selected text.
|
||||||
void _applyLink() {
|
void _applyLink() {
|
||||||
final url = _urlController.text;
|
final url = _urlController.text.text;
|
||||||
|
|
||||||
final selection = widget.composer.selection;
|
final selection = widget.composer.selection;
|
||||||
final baseOffset = (selection.base.nodePosition as TextPosition).offset;
|
final baseOffset = (selection.base.nodePosition as TextPosition).offset;
|
||||||
|
|
@ -360,7 +364,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
final selectionStart = min(baseOffset, extentOffset);
|
final selectionStart = min(baseOffset, extentOffset);
|
||||||
final selectionEnd = max(baseOffset, extentOffset);
|
final selectionEnd = max(baseOffset, extentOffset);
|
||||||
final selectionRange =
|
final selectionRange =
|
||||||
SpanRange(start: selectionStart, end: selectionEnd - 1);
|
TextRange(start: selectionStart, end: selectionEnd - 1);
|
||||||
|
|
||||||
final textNode =
|
final textNode =
|
||||||
widget.editor.document.getNodeById(selection.extent.nodeId) as TextNode;
|
widget.editor.document.getNodeById(selection.extent.nodeId) as TextNode;
|
||||||
|
|
@ -387,7 +391,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
/// Given [text] and a [range] within the [text], the [range] is
|
/// Given [text] and a [range] within the [text], the [range] is
|
||||||
/// shortened on both sides to remove any trailing whitespace and
|
/// shortened on both sides to remove any trailing whitespace and
|
||||||
/// the new range is returned.
|
/// the new range is returned.
|
||||||
SpanRange _trimTextRangeWhitespace(AttributedText text, SpanRange range) {
|
SpanRange _trimTextRangeWhitespace(AttributedText text, TextRange range) {
|
||||||
int startOffset = range.start;
|
int startOffset = range.start;
|
||||||
int endOffset = range.end;
|
int endOffset = range.end;
|
||||||
|
|
||||||
|
|
@ -504,6 +508,9 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
|
/*
|
||||||
|
// https://github.com/superlistapp/super_editor/issues/689
|
||||||
|
// https://github.com/flutter/flutter/issues/106923
|
||||||
// Only allow the user to select a new type of text node if
|
// Only allow the user to select a new type of text node if
|
||||||
// the currently selected node can be converted.
|
// the currently selected node can be converted.
|
||||||
if (_isConvertibleNode()) ...[
|
if (_isConvertibleNode()) ...[
|
||||||
|
|
@ -512,7 +519,6 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
child: DropdownButton<_TextType>(
|
child: DropdownButton<_TextType>(
|
||||||
value: _getCurrentTextType(),
|
value: _getCurrentTextType(),
|
||||||
items: _TextType.values
|
items: _TextType.values
|
||||||
.where((element) => element != _TextType.blockquote)
|
|
||||||
.map((textType) => DropdownMenuItem<_TextType>(
|
.map((textType) => DropdownMenuItem<_TextType>(
|
||||||
value: textType,
|
value: textType,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -522,8 +528,10 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
icon: const Icon(Icons.arrow_drop_down),
|
icon: const Icon(Icons.arrow_drop_down),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
underline: const SizedBox(),
|
underline: const SizedBox(),
|
||||||
|
|
@ -534,6 +542,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
),
|
),
|
||||||
_buildVerticalDivider(),
|
_buildVerticalDivider(),
|
||||||
],
|
],
|
||||||
|
*/
|
||||||
Center(
|
Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: _toggleBold,
|
onPressed: _toggleBold,
|
||||||
|
|
@ -604,6 +613,15 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
_buildVerticalDivider(),
|
||||||
|
Center(
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.more_vert),
|
||||||
|
splashRadius: 16,
|
||||||
|
tooltip: 'More Options',
|
||||||
|
),
|
||||||
|
),
|
||||||
*/
|
*/
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -623,14 +641,37 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: FocusWithCustomParent(
|
||||||
focusNode: _urlFocusNode,
|
focusNode: _urlFocusNode,
|
||||||
controller: _urlController,
|
parentFocusNode: widget.editorFocusNode,
|
||||||
decoration: const InputDecoration(
|
// We use a SuperTextField instead of a TextField because TextField
|
||||||
hintText: 'enter url...',
|
// automatically re-parents its FocusNode, which causes #609. Flutter
|
||||||
border: InputBorder.none,
|
// #106923 tracks the TextField issue.
|
||||||
|
child: SuperTextField(
|
||||||
|
focusNode: _urlFocusNode,
|
||||||
|
textController: _urlController,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 1,
|
||||||
|
inputSource: TextInputSource.ime,
|
||||||
|
hintBehavior: HintBehavior.displayHintUntilTextEntered,
|
||||||
|
hintBuilder: (context) {
|
||||||
|
return Text(
|
||||||
|
'Enter a url...',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
textStyleBuilder: (_) {
|
||||||
|
return TextStyle(
|
||||||
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onSubmitted: (newValue) => _applyLink(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|
@ -840,3 +881,21 @@ class _PositionedToolbar extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SingleLineAttributedTextEditingController
|
||||||
|
extends AttributedTextEditingController {
|
||||||
|
SingleLineAttributedTextEditingController(this.onSubmit);
|
||||||
|
|
||||||
|
final VoidCallback onSubmit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void insertNewline() {
|
||||||
|
// Don't insert newline in a single-line text field.
|
||||||
|
|
||||||
|
// Invoke callback to take action on enter.
|
||||||
|
onSubmit();
|
||||||
|
|
||||||
|
// TODO: this is a hack. SuperTextField shouldn't insert newlines in a single
|
||||||
|
// line field (#697).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@ import Foundation
|
||||||
import in_app_purchase_storekit
|
import in_app_purchase_storekit
|
||||||
import in_app_review
|
import in_app_review
|
||||||
import package_info
|
import package_info
|
||||||
import package_info_plus_macos
|
import package_info_plus
|
||||||
import path_provider_macos
|
import path_provider_foundation
|
||||||
import printing
|
import printing
|
||||||
import screen_retriever
|
import screen_retriever
|
||||||
import sentry_flutter
|
import sentry_flutter
|
||||||
import shared_preferences_macos
|
import share_plus
|
||||||
|
import shared_preferences_foundation
|
||||||
import sign_in_with_apple
|
import sign_in_with_apple
|
||||||
import smart_auth
|
import smart_auth
|
||||||
import sqflite
|
import sqflite
|
||||||
|
|
@ -29,6 +30,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||||
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
||||||
SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin"))
|
SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin"))
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,16 @@ dependencies:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
# google_sign_in: ^5.0.7
|
# google_sign_in: ^6.0.1
|
||||||
# in_app_review: ^2.0.4
|
# in_app_review: ^2.0.4
|
||||||
# in_app_purchase: ^3.1.1
|
# in_app_purchase: ^3.1.1
|
||||||
# pinput: ^2.2.11
|
# pinput: ^2.2.11
|
||||||
flutter_redux: ^0.8.2
|
flutter_redux: ^0.10.0
|
||||||
redux_logging: ^0.5.0
|
redux_logging: ^0.5.0
|
||||||
http: ^0.13.3
|
http: ^0.13.3
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
material_design_icons_flutter: ^5.0.6295
|
material_design_icons_flutter: ^6.0.7096
|
||||||
built_value: ^8.1.2
|
built_value: ^8.1.2
|
||||||
built_collection: ^5.1.0
|
built_collection: ^5.1.0
|
||||||
memoize: ^3.0.0
|
memoize: ^3.0.0
|
||||||
|
|
@ -37,46 +37,46 @@ dependencies:
|
||||||
url_launcher: ^6.0.20
|
url_launcher: ^6.0.20
|
||||||
share: ^2.0.4
|
share: ^2.0.4
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
flutter_slidable: ^1.1.0
|
flutter_slidable: ^2.0.0
|
||||||
charts_flutter: ^0.12.0
|
charts_flutter: ^0.12.0
|
||||||
#qr_flutter: ^4.0.0
|
#qr_flutter: ^4.0.0
|
||||||
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
||||||
git:
|
git:
|
||||||
url: https://github.com/theyakka/qr.flutter.git
|
url: https://github.com/theyakka/qr.flutter.git
|
||||||
local_auth: ^1.1.6
|
local_auth: ^2.1.5
|
||||||
sentry_flutter: ^6.5.1
|
sentry_flutter: ^6.20.1
|
||||||
image_picker: ^0.8.3+1
|
image_picker: ^0.8.6+3
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
flutter_json_viewer: ^1.0.1
|
flutter_json_viewer: ^1.0.1
|
||||||
webview_flutter: ^3.0.4
|
webview_flutter: ^3.0.4
|
||||||
timeago: ^3.1.0
|
timeago: ^3.1.0
|
||||||
package_info: ^2.0.2
|
package_info: ^2.0.2
|
||||||
rounded_loading_button: ^2.0.5
|
rounded_loading_button: ^2.1.0
|
||||||
version: ^2.0.0
|
version: ^3.0.2
|
||||||
flutter_launcher_icons: ^0.9.1
|
# flutter_launcher_icons: ^0.9.1
|
||||||
overflow_view: ^0.3.1
|
overflow_view: ^0.3.1
|
||||||
flutter_styled_toast: ^2.0.0
|
flutter_styled_toast: ^2.0.0
|
||||||
permission_handler: ^9.2.0
|
permission_handler: ^10.2.0
|
||||||
file_picker: ^4.2.3
|
file_picker: ^5.2.5
|
||||||
boardview: ^0.2.2
|
boardview: ^0.2.2
|
||||||
pointer_interceptor: ^0.9.0
|
pointer_interceptor: ^0.9.0
|
||||||
contacts_service: ^0.6.1
|
contacts_service: ^0.6.3
|
||||||
diacritic: ^0.1.3
|
diacritic: ^0.1.3
|
||||||
states_rebuilder: ^5.2.0
|
states_rebuilder: ^6.2.0
|
||||||
#super_editor: ^0.2.2
|
# super_editor: ^0.2.2
|
||||||
markdown: ^5.0.0 # REMOVE THIS
|
markdown: ^5.0.0 # REMOVE THIS
|
||||||
super_editor:
|
super_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/superlistapp/super_editor.git
|
url: https://github.com/superlistapp/super_editor.git
|
||||||
path: super_editor
|
path: super_editor
|
||||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||||
html2md: ^1.2.5
|
html2md: ^1.2.6
|
||||||
printing: ^5.8.0
|
printing: ^5.10.1
|
||||||
image_cropper: ^2.0.2
|
image_cropper: ^3.0.1
|
||||||
msal_js: ^2.14.0
|
msal_js: ^2.14.0
|
||||||
sign_in_with_apple: ^4.0.0
|
sign_in_with_apple: ^4.3.0
|
||||||
window_manager: ^0.2.7
|
window_manager: ^0.3.0
|
||||||
# bitsdojo_window: ^0.1.2
|
# bitsdojo_window: ^0.1.5
|
||||||
intl_phone_field: ^3.1.0
|
intl_phone_field: ^3.1.0
|
||||||
flutter_staggered_grid_view: ^0.6.2
|
flutter_staggered_grid_view: ^0.6.2
|
||||||
# quick_actions: ^0.2.1
|
# quick_actions: ^0.2.1
|
||||||
|
|
|
||||||
1095
pubspec.lock
1095
pubspec.lock
File diff suppressed because it is too large
Load Diff
|
|
@ -44,39 +44,39 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/theyakka/qr.flutter.git
|
url: https://github.com/theyakka/qr.flutter.git
|
||||||
local_auth: ^1.1.6
|
local_auth: ^1.1.6
|
||||||
sentry_flutter: ^6.5.1
|
sentry_flutter: ^6.20.1
|
||||||
image_picker: ^0.8.3+1
|
image_picker: ^0.8.6+3
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
flutter_json_viewer: ^1.0.1
|
flutter_json_viewer: ^1.0.1
|
||||||
webview_flutter: ^2.0.10
|
webview_flutter: ^2.0.10
|
||||||
timeago: ^3.1.0
|
timeago: ^3.1.0
|
||||||
package_info: ^2.0.2
|
package_info: ^2.0.2
|
||||||
rounded_loading_button: ^2.0.5
|
rounded_loading_button: ^2.1.0
|
||||||
version: ^2.0.0
|
version: ^2.0.0
|
||||||
flutter_launcher_icons: ^0.9.1
|
# flutter_launcher_icons: ^0.9.1
|
||||||
overflow_view: ^0.3.1
|
overflow_view: ^0.3.1
|
||||||
flutter_styled_toast: ^2.0.0
|
flutter_styled_toast: ^2.0.0
|
||||||
permission_handler: ^9.2.0
|
permission_handler: ^9.2.0
|
||||||
file_picker: ^4.2.3
|
file_picker: ^5.2.5
|
||||||
boardview: ^0.2.2
|
boardview: ^0.2.2
|
||||||
pointer_interceptor: ^0.9.0
|
pointer_interceptor: ^0.9.0
|
||||||
contacts_service: ^0.6.1
|
contacts_service: ^0.6.3
|
||||||
diacritic: ^0.1.3
|
diacritic: ^0.1.3
|
||||||
states_rebuilder: ^5.2.0
|
states_rebuilder: ^5.2.0
|
||||||
#super_editor: ^0.2.0
|
# super_editor: ^0.2.0
|
||||||
markdown: ^5.0.0 # REMOVE THIS
|
markdown: ^5.0.0 # REMOVE THIS
|
||||||
super_editor:
|
super_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/superlistapp/super_editor.git
|
url: https://github.com/superlistapp/super_editor.git
|
||||||
path: super_editor
|
path: super_editor
|
||||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||||
html2md: ^1.2.5
|
html2md: ^1.2.6
|
||||||
printing: ^5.8.0
|
printing: ^5.10.1
|
||||||
image_cropper: ^2.0.2
|
image_cropper: ^3.0.1
|
||||||
msal_js: ^2.14.0
|
msal_js: ^2.14.0
|
||||||
sign_in_with_apple: ^4.0.0
|
sign_in_with_apple: ^4.3.0
|
||||||
window_manager: ^0.2.5
|
window_manager: ^0.3.0
|
||||||
bitsdojo_window: ^0.1.2
|
# bitsdojo_window: ^0.1.5
|
||||||
intl_phone_field: ^3.1.0
|
intl_phone_field: ^3.1.0
|
||||||
flutter_staggered_grid_view: ^0.6.2
|
flutter_staggered_grid_view: ^0.6.2
|
||||||
# quick_actions: ^0.2.1
|
# quick_actions: ^0.2.1
|
||||||
|
|
|
||||||
46
pubspec.yaml
46
pubspec.yaml
|
|
@ -20,7 +20,7 @@ dependencies:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
google_sign_in: ^5.0.7
|
google_sign_in: ^6.0.1
|
||||||
in_app_review: ^2.0.4
|
in_app_review: ^2.0.4
|
||||||
in_app_purchase: ^3.1.1
|
in_app_purchase: ^3.1.1
|
||||||
pinput: ^2.2.11
|
pinput: ^2.2.11
|
||||||
|
|
@ -29,59 +29,59 @@ dependencies:
|
||||||
# git:
|
# git:
|
||||||
# url: https://github.com/Tkko/Flutter_Pinput
|
# url: https://github.com/Tkko/Flutter_Pinput
|
||||||
# ref: dev
|
# ref: dev
|
||||||
flutter_redux: ^0.8.2
|
flutter_redux: ^0.10.0
|
||||||
redux_logging: ^0.5.0
|
redux_logging: ^0.5.0
|
||||||
http: ^0.13.3
|
http: ^0.13.3
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
material_design_icons_flutter: ^5.0.6295
|
material_design_icons_flutter: ^6.0.7096
|
||||||
built_value: ^8.1.2
|
built_value: ^8.1.2
|
||||||
built_collection: ^5.1.0
|
built_collection: ^5.1.0
|
||||||
memoize: ^3.0.0
|
memoize: ^3.0.0
|
||||||
cached_network_image: 3.0.0 # imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
|
cached_network_image: 3.0.0 # imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
|
||||||
url_launcher: ^6.0.20
|
url_launcher: ^6.0.20
|
||||||
share: ^2.0.4
|
share_plus: ^6.3.1
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
flutter_slidable: ^1.1.0
|
flutter_slidable: ^2.0.0
|
||||||
charts_flutter: ^0.12.0
|
charts_flutter: ^0.12.0
|
||||||
#qr_flutter: ^4.0.0
|
#qr_flutter: ^4.0.0
|
||||||
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
||||||
git:
|
git:
|
||||||
url: https://github.com/theyakka/qr.flutter.git
|
url: https://github.com/theyakka/qr.flutter.git
|
||||||
local_auth: ^1.1.6
|
local_auth: ^2.1.5
|
||||||
sentry_flutter: ^6.9.1
|
sentry_flutter: ^6.20.1
|
||||||
image_picker: ^0.8.3+1
|
image_picker: ^0.8.6+3
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
flutter_json_viewer: ^1.0.1
|
flutter_json_viewer: ^1.0.1
|
||||||
webview_flutter: ^3.0.4
|
webview_flutter: ^3.0.4
|
||||||
timeago: ^3.1.0
|
timeago: ^3.1.0
|
||||||
package_info: ^2.0.2
|
package_info: ^2.0.2
|
||||||
rounded_loading_button: ^2.0.5
|
rounded_loading_button: ^2.1.0
|
||||||
version: ^2.0.0
|
version: ^3.0.2
|
||||||
flutter_launcher_icons: ^0.9.1
|
# flutter_launcher_icons: ^0.9.1
|
||||||
overflow_view: ^0.3.1
|
overflow_view: ^0.3.1
|
||||||
flutter_styled_toast: ^2.0.0
|
flutter_styled_toast: ^2.0.0
|
||||||
permission_handler: ^9.2.0
|
permission_handler: ^10.2.0
|
||||||
file_picker: ^4.2.3
|
file_picker: ^5.2.5
|
||||||
boardview: ^0.2.2
|
boardview: ^0.2.2
|
||||||
pointer_interceptor: ^0.9.0
|
pointer_interceptor: ^0.9.0
|
||||||
contacts_service: ^0.6.1
|
contacts_service: ^0.6.3
|
||||||
diacritic: ^0.1.3
|
diacritic: ^0.1.3
|
||||||
states_rebuilder: ^5.2.0
|
states_rebuilder: ^6.2.0
|
||||||
#super_editor: ^0.2.2
|
# super_editor: ^0.2.2
|
||||||
markdown: ^5.0.0 # REMOVE THIS
|
markdown: ^5.0.0 # REMOVE THIS
|
||||||
super_editor:
|
super_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/superlistapp/super_editor.git
|
url: https://github.com/superlistapp/super_editor.git
|
||||||
path: super_editor
|
path: super_editor
|
||||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
#ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||||
html2md: ^1.2.5
|
html2md: ^1.2.6
|
||||||
printing: ^5.8.0
|
printing: ^5.10.1
|
||||||
image_cropper: ^2.0.2
|
image_cropper: ^3.0.1
|
||||||
msal_js: ^2.14.0
|
msal_js: ^2.14.0
|
||||||
sign_in_with_apple: ^4.0.0
|
sign_in_with_apple: ^4.3.0
|
||||||
window_manager: ^0.2.7
|
window_manager: ^0.3.0
|
||||||
# bitsdojo_window: ^0.1.2
|
# bitsdojo_window: ^0.1.5
|
||||||
intl_phone_field: ^3.1.0
|
intl_phone_field: ^3.1.0
|
||||||
flutter_staggered_grid_view: ^0.6.2
|
flutter_staggered_grid_view: ^0.6.2
|
||||||
# quick_actions: ^0.2.1
|
# quick_actions: ^0.2.1
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,19 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <local_auth_windows/local_auth_plugin.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <printing/printing_plugin.h>
|
#include <printing/printing_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||||
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <smart_auth/smart_auth_plugin.h>
|
#include <smart_auth/smart_auth_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
LocalAuthPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
PrintingPluginRegisterWithRegistrar(
|
PrintingPluginRegisterWithRegistrar(
|
||||||
|
|
@ -23,6 +27,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||||
SentryFlutterPluginRegisterWithRegistrar(
|
SentryFlutterPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
|
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
|
||||||
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
SmartAuthPluginRegisterWithRegistrar(
|
SmartAuthPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SmartAuthPlugin"));
|
registry->GetRegistrarForPlugin("SmartAuthPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
local_auth_windows
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
printing
|
printing
|
||||||
screen_retriever
|
screen_retriever
|
||||||
sentry_flutter
|
sentry_flutter
|
||||||
|
share_plus
|
||||||
smart_auth
|
smart_auth
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
window_manager
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,20 @@ add_executable(${BINARY_NAME} WIN32
|
||||||
# that need different build settings.
|
# that need different build settings.
|
||||||
apply_standard_settings(${BINARY_NAME})
|
apply_standard_settings(${BINARY_NAME})
|
||||||
|
|
||||||
|
# Add preprocessor definitions for the build version.
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
|
||||||
|
|
||||||
# Disable Windows macros that collide with C++ standard library functions.
|
# Disable Windows macros that collide with C++ standard library functions.
|
||||||
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||||
|
|
||||||
# Add dependency libraries and include directories. Add any application-specific
|
# Add dependency libraries and include directories. Add any application-specific
|
||||||
# dependencies here.
|
# dependencies here.
|
||||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
|
||||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||||
|
|
||||||
# Run the Flutter tool portions of the build. This must not be removed.
|
# Run the Flutter tool portions of the build. This must not be removed.
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
|
||||||
// Version
|
// Version
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef FLUTTER_BUILD_NUMBER
|
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
|
||||||
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
|
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
|
||||||
#else
|
#else
|
||||||
#define VERSION_AS_NUMBER 1,0,0
|
#define VERSION_AS_NUMBER 1,0,0,0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FLUTTER_BUILD_NAME
|
#if defined(FLUTTER_VERSION)
|
||||||
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
|
#define VERSION_AS_STRING FLUTTER_VERSION
|
||||||
#else
|
#else
|
||||||
#define VERSION_AS_STRING "1.0.0"
|
#define VERSION_AS_STRING "1.0.0"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -93,7 +93,7 @@ BEGIN
|
||||||
VALUE "FileDescription", "Invoice Ninja" "\0"
|
VALUE "FileDescription", "Invoice Ninja" "\0"
|
||||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||||
VALUE "InternalName", "invoiceninja" "\0"
|
VALUE "InternalName", "invoiceninja" "\0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2021 Invoice Ninja. All rights reserved." "\0"
|
VALUE "LegalCopyright", "Copyright (C) 2023 Invoice Ninja. All rights reserved." "\0"
|
||||||
VALUE "OriginalFilename", "invoiceninja.exe" "\0"
|
VALUE "OriginalFilename", "invoiceninja.exe" "\0"
|
||||||
VALUE "ProductName", "invoiceninja" "\0"
|
VALUE "ProductName", "invoiceninja" "\0"
|
||||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ bool FlutterWindow::OnCreate() {
|
||||||
}
|
}
|
||||||
RegisterPlugins(flutter_controller_->engine());
|
RegisterPlugins(flutter_controller_->engine());
|
||||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||||
|
|
||||||
|
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||||
|
this->Show();
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
//#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
|
||||||
//auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP);
|
|
||||||
|
|
||||||
#include <flutter/dart_project.h>
|
#include <flutter/dart_project.h>
|
||||||
#include <flutter/flutter_view_controller.h>
|
#include <flutter/flutter_view_controller.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -30,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
Win32Window::Point origin(10, 10);
|
Win32Window::Point origin(10, 10);
|
||||||
Win32Window::Size size(1280, 720);
|
Win32Window::Size size(1280, 720);
|
||||||
if (!window.CreateAndShow(L"Invoice Ninja", origin, size)) {
|
if (!window.Create(L"Invoice Ninja", origin, size)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
window.SetQuitOnClose(true);
|
window.SetQuitOnClose(true);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</application>
|
</application>
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
<application>
|
<application>
|
||||||
<!-- Windows 10 -->
|
<!-- Windows 10 and Windows 11 -->
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
<!-- Windows 8.1 -->
|
<!-- Windows 8.1 -->
|
||||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
#include "win32_window.h"
|
#include "win32_window.h"
|
||||||
|
|
||||||
|
#include <dwmapi.h>
|
||||||
#include <flutter_windows.h>
|
#include <flutter_windows.h>
|
||||||
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/// Window attribute that enables dark mode window decorations.
|
||||||
|
///
|
||||||
|
/// Redefined in case the developer's machine has a Windows SDK older than
|
||||||
|
/// version 10.0.22000.0.
|
||||||
|
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||||
|
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||||
|
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||||
|
|
||||||
|
/// Registry key for app theme preference.
|
||||||
|
///
|
||||||
|
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
|
||||||
|
/// value indicates apps should use light mode.
|
||||||
|
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
|
||||||
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||||
|
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
|
||||||
|
|
||||||
// The number of Win32Window objects that currently exist.
|
// The number of Win32Window objects that currently exist.
|
||||||
static int g_active_window_count = 0;
|
static int g_active_window_count = 0;
|
||||||
|
|
||||||
|
|
@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
||||||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||||
if (enable_non_client_dpi_scaling != nullptr) {
|
if (enable_non_client_dpi_scaling != nullptr) {
|
||||||
enable_non_client_dpi_scaling(hwnd);
|
enable_non_client_dpi_scaling(hwnd);
|
||||||
FreeLibrary(user32_module);
|
|
||||||
}
|
}
|
||||||
|
FreeLibrary(user32_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -102,9 +120,9 @@ Win32Window::~Win32Window() {
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Win32Window::CreateAndShow(const std::wstring& title,
|
bool Win32Window::Create(const std::wstring& title,
|
||||||
const Point& origin,
|
const Point& origin,
|
||||||
const Size& size) {
|
const Size& size) {
|
||||||
Destroy();
|
Destroy();
|
||||||
|
|
||||||
const wchar_t* window_class =
|
const wchar_t* window_class =
|
||||||
|
|
@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||||
double scale_factor = dpi / 96.0;
|
double scale_factor = dpi / 96.0;
|
||||||
|
|
||||||
HWND window = CreateWindow(
|
HWND window = CreateWindow(
|
||||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
|
||||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||||
|
|
@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateTheme(window);
|
||||||
|
|
||||||
return OnCreate();
|
return OnCreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Win32Window::Show() {
|
||||||
|
return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||||
UINT const message,
|
UINT const message,
|
||||||
|
|
@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd,
|
||||||
SetFocus(child_content_);
|
SetFocus(child_content_);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||||
|
UpdateTheme(hwnd);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(window_handle_, message, wparam, lparam);
|
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||||
|
|
@ -243,3 +271,18 @@ bool Win32Window::OnCreate() {
|
||||||
void Win32Window::OnDestroy() {
|
void Win32Window::OnDestroy() {
|
||||||
// No-op; provided for subclasses.
|
// No-op; provided for subclasses.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Win32Window::UpdateTheme(HWND const window) {
|
||||||
|
DWORD light_mode;
|
||||||
|
DWORD light_mode_size = sizeof(light_mode);
|
||||||
|
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
|
||||||
|
kGetPreferredBrightnessRegValue,
|
||||||
|
RRF_RT_REG_DWORD, nullptr, &light_mode,
|
||||||
|
&light_mode_size);
|
||||||
|
|
||||||
|
if (result == ERROR_SUCCESS) {
|
||||||
|
BOOL enable_dark_mode = light_mode == 0;
|
||||||
|
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||||
|
&enable_dark_mode, sizeof(enable_dark_mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,16 @@ class Win32Window {
|
||||||
Win32Window();
|
Win32Window();
|
||||||
virtual ~Win32Window();
|
virtual ~Win32Window();
|
||||||
|
|
||||||
// Creates and shows a win32 window with |title| and position and size using
|
// Creates a win32 window with |title| that is positioned and sized using
|
||||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||||
// consistent size to will treat the width height passed in to this function
|
// consistent size this function will scale the inputted width and height as
|
||||||
// as logical pixels and scale to appropriate for the default monitor. Returns
|
// as appropriate for the default monitor. The window is invisible until
|
||||||
// true if the window was created successfully.
|
// |Show| is called. Returns true if the window was created successfully.
|
||||||
bool CreateAndShow(const std::wstring& title,
|
bool Create(const std::wstring& title, const Point& origin, const Size& size);
|
||||||
const Point& origin,
|
|
||||||
const Size& size);
|
// Show the current window. Returns true if the window was successfully shown.
|
||||||
|
bool Show();
|
||||||
|
|
||||||
// Release OS resources associated with window.
|
// Release OS resources associated with window.
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
@ -86,6 +87,9 @@ class Win32Window {
|
||||||
// Retrieves a class instance pointer for |window|
|
// Retrieves a class instance pointer for |window|
|
||||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||||
|
|
||||||
|
// Update the window frame's theme to match the system theme.
|
||||||
|
static void UpdateTheme(HWND const window);
|
||||||
|
|
||||||
bool quit_on_close_ = false;
|
bool quit_on_close_ = false;
|
||||||
|
|
||||||
// window handle for top level window.
|
// window handle for top level window.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue