Flutter upgrade

This commit is contained in:
Hillel Coren 2023-12-07 11:40:16 +02:00
parent 3131e0ed62
commit 5669b36fb7
22 changed files with 784 additions and 691 deletions

View File

@ -413,6 +413,7 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
*/ */
theme: state.prefState.enableDarkMode theme: state.prefState.enableDarkMode
? ThemeData( ? ThemeData(
useMaterial3: false,
tooltipTheme: TooltipThemeData( tooltipTheme: TooltipThemeData(
waitDuration: Duration(milliseconds: 500), waitDuration: Duration(milliseconds: 500),
), ),
@ -438,6 +439,7 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
BottomAppBarTheme(color: const Color(0xFF1B1C1E)), BottomAppBarTheme(color: const Color(0xFF1B1C1E)),
) )
: ThemeData( : ThemeData(
useMaterial3: false,
tooltipTheme: TooltipThemeData( tooltipTheme: TooltipThemeData(
waitDuration: Duration(milliseconds: 500), waitDuration: Duration(milliseconds: 500),
), ),

View File

@ -162,7 +162,7 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
} }
final companies = final companies =
list.where((CompanyEntity company) => (company.id).isNotEmpty).toList(); list.where((CompanyEntity company) => company.id.isNotEmpty).toList();
return companies; return companies;
} }

View File

@ -191,7 +191,7 @@ List<TaskEntity?> _runningTasks({
} }
}); });
tasks.sort((taskA, taskB) => (taskB.updatedAt).compareTo(taskA.updatedAt)); tasks.sort((taskA, taskB) => taskB.updatedAt.compareTo(taskA.updatedAt));
return tasks; return tasks;
} }
@ -219,7 +219,7 @@ List<TaskEntity> _recentTasks({
} }
}); });
tasks.sort((taskA, taskB) => (taskB.updatedAt).compareTo(taskA.updatedAt)); tasks.sort((taskA, taskB) => taskB.updatedAt.compareTo(taskA.updatedAt));
return tasks; return tasks;
} }

View File

@ -388,8 +388,7 @@ void handleRecurringExpenseAction(BuildContext? context,
break; break;
case EntityAction.start: case EntityAction.start:
store.dispatch(StartRecurringExpensesRequest( store.dispatch(StartRecurringExpensesRequest(
completer: snackBarCompleter<Null>( completer: snackBarCompleter<Null>(recurringExpense.lastSentDate.isEmpty
(recurringExpense.lastSentDate).isEmpty
? localization!.startedRecurringInvoice ? localization!.startedRecurringInvoice
: localization!.resumedRecurringInvoice), : localization!.resumedRecurringInvoice),
expenseIds: recurringExpenseIds, expenseIds: recurringExpenseIds,

View File

@ -629,8 +629,7 @@ void handleRecurringInvoiceAction(BuildContext? context,
break; break;
case EntityAction.start: case EntityAction.start:
store.dispatch(StartRecurringInvoicesRequest( store.dispatch(StartRecurringInvoicesRequest(
completer: snackBarCompleter<Null>( completer: snackBarCompleter<Null>(recurringInvoice.lastSentDate.isEmpty
(recurringInvoice.lastSentDate).isEmpty
? localization!.startedRecurringInvoice ? localization!.startedRecurringInvoice
: localization!.resumedRecurringInvoice), : localization!.resumedRecurringInvoice),
invoiceIds: recurringInvoiceIds, invoiceIds: recurringInvoiceIds,

View File

@ -300,7 +300,7 @@ void handleVendorAction(
break; break;
case EntityAction.vendorPortal: case EntityAction.vendorPortal:
final contact = vendor.contacts.firstWhere((contact) { final contact = vendor.contacts.firstWhere((contact) {
return (contact.link).isNotEmpty; return contact.link.isNotEmpty;
}, orElse: null); }, orElse: null);
launchUrl(Uri.parse(contact.silentLink)); launchUrl(Uri.parse(contact.silentLink));
break; break;

View File

@ -74,7 +74,7 @@ class MultiSelectListState extends State<MultiSelectList> {
void initState() { void initState() {
super.initState(); super.initState();
selected = selected =
(widget.selected).isNotEmpty ? widget.selected : widget.defaultSelected; widget.selected.isNotEmpty ? widget.selected : widget.defaultSelected;
_controller = ScrollController(); _controller = ScrollController();
} }

View File

@ -459,7 +459,7 @@ class DocumentPreview extends StatelessWidget {
key: ValueKey(document.preview), key: ValueKey(document.preview),
imageUrl: imageUrl:
'${cleanApiUrl(state.credentials.url)}/documents/${document.hash}', '${cleanApiUrl(state.credentials.url)}/documents/${document.hash}',
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, //imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
httpHeaders: {'X-API-TOKEN': state.credentials.token}, httpHeaders: {'X-API-TOKEN': state.credentials.token},
placeholder: (context, url) => Container( placeholder: (context, url) => Container(
height: height, height: height,

View File

@ -87,7 +87,7 @@ class _InvoiceEmailViewState extends State<InvoiceEmailView>
selectedTemplate = EmailTemplate.reminder3; selectedTemplate = EmailTemplate.reminder3;
else if ((invoice.reminder1Sent ?? '').isNotEmpty) else if ((invoice.reminder1Sent ?? '').isNotEmpty)
selectedTemplate = EmailTemplate.reminder2; selectedTemplate = EmailTemplate.reminder2;
else if ((invoice.lastSentDate).isNotEmpty) else if (invoice.lastSentDate.isNotEmpty)
selectedTemplate = EmailTemplate.reminder1; selectedTemplate = EmailTemplate.reminder1;
else else
selectedTemplate = EmailTemplate.invoice; selectedTemplate = EmailTemplate.invoice;

View File

@ -139,7 +139,7 @@ class ActivityListTile extends StatelessWidget {
subtitle: Row( subtitle: Row(
children: <Widget>[ children: <Widget>[
Flexible( Flexible(
child: Text(((activity.notes).isNotEmpty child: Text((activity.notes.isNotEmpty
? localization.lookup(activity.notes).trim() + '\n' ? localization.lookup(activity.notes).trim() + '\n'
: '') + : '') +
formatDate( formatDate(

View File

@ -165,7 +165,7 @@ class ClientOverview extends StatelessWidget {
), ),
), ),
ListDivider(), ListDivider(),
if ((client.privateNotes).isNotEmpty) ...[ if (client.privateNotes.isNotEmpty) ...[
IconMessage(client.privateNotes, iconData: Icons.lock), IconMessage(client.privateNotes, iconData: Icons.lock),
ListDivider() ListDivider()
], ],

View File

@ -129,7 +129,7 @@ class _InvoiceItemSelectorState extends State<InvoiceItemSelector>
if (selected != null) { if (selected != null) {
_filterClientId = (selected as BelongsToClient).clientId; _filterClientId = (selected as BelongsToClient).clientId;
} else if ((widget.clientId).isEmpty) { } else if (widget.clientId.isEmpty) {
_filterClientId = null; _filterClientId = null;
} }
} }

View File

@ -217,7 +217,7 @@ class PurchaseOrderListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: filterMatch == null child: filterMatch == null
? Text((((purchaseOrder.number).isEmpty ? Text(((purchaseOrder.number.isEmpty
? localization!.pending ? localization!.pending
: purchaseOrder.number) + : purchaseOrder.number) +
'' + '' +

View File

@ -73,7 +73,7 @@ class PurchaseOrderPresenter extends EntityPresenter {
case PurchaseOrderFields.status: case PurchaseOrderFields.status:
return EntityStatusChip(entity: purchaseOrder, showState: true); return EntityStatusChip(entity: purchaseOrder, showState: true);
case PurchaseOrderFields.number: case PurchaseOrderFields.number:
return Text((purchaseOrder.number).isEmpty return Text(purchaseOrder.number.isEmpty
? localization!.pending ? localization!.pending
: purchaseOrder.number); : purchaseOrder.number);
case PurchaseOrderFields.client: case PurchaseOrderFields.client:

View File

@ -113,7 +113,7 @@ class QuoteListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(quote.number).isEmpty quote.number.isEmpty
? localization!.pending ? localization!.pending
: quote.number, : quote.number,
style: textStyle, style: textStyle,

View File

@ -122,7 +122,7 @@ class RecurringInvoiceListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(invoice.number).isEmpty invoice.number.isEmpty
? localization.pending ? localization.pending
: invoice.number, : invoice.number,
style: textStyle, style: textStyle,

View File

@ -1,3 +1,4 @@
/*
// DELETE THIS FILE ONCE SUPER EDITOR IS UPDATED // DELETE THIS FILE ONCE SUPER EDITOR IS UPDATED
// Note: using the standard function crashes with h1 tags // Note: using the standard function crashes with h1 tags
@ -588,3 +589,4 @@ class _EmptyParagraphSyntax extends md.BlockSyntax {
return md.Element('p', []); return md.Element('p', []);
} }
} }
*/

View File

@ -1,8 +1,11 @@
//import 'package:example/logging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/utils/markdown.dart';
import 'package:invoiceninja_flutter/utils/super_editor/toolbar.dart';
import 'package:super_editor/super_editor.dart'; import 'package:super_editor/super_editor.dart';
import 'package:super_editor_markdown/super_editor_markdown.dart';
import 'package:invoiceninja_flutter/utils/super_editor/toolbar.dart';
//import '_toolbar.dart';
/// Example of a rich text editor. /// Example of a rich text editor.
/// ///
@ -19,39 +22,46 @@ class ExampleEditor extends StatefulWidget {
final Function(String)? onChanged; final Function(String)? onChanged;
@override @override
_ExampleEditorState createState() => _ExampleEditorState(); State<ExampleEditor> createState() => _ExampleEditorState();
} }
class _ExampleEditorState extends State<ExampleEditor> { class _ExampleEditorState extends State<ExampleEditor> {
final GlobalKey _viewportKey = GlobalKey();
final GlobalKey _docLayoutKey = GlobalKey(); final GlobalKey _docLayoutKey = GlobalKey();
late Document _doc; late MutableDocument _doc;
DocumentEditor? _docEditor; final _docChangeSignal = SignalNotifier();
DocumentComposer? _composer; late MutableDocumentComposer _composer;
late Editor _docEditor;
late CommonEditorOperations _docOps; late CommonEditorOperations _docOps;
FocusNode? _editorFocusNode; late FocusNode _editorFocusNode;
ScrollController? _scrollController; late ScrollController _scrollController;
final SelectionLayerLinks _selectionLayerLinks = SelectionLayerLinks();
final _darkBackground = const Color(0xFF222222); final _darkBackground = const Color(0xFF222222);
final _lightBackground = Colors.white; final _lightBackground = Colors.white;
//Brightness _brightness = Brightness.light; final _brightness = ValueNotifier<Brightness>(Brightness.light);
SuperEditorDebugVisualsConfig? _debugConfig; SuperEditorDebugVisualsConfig? _debugConfig;
OverlayEntry? _textFormatBarOverlayEntry; final _textFormatBarOverlayController = OverlayPortalController();
final _textSelectionAnchor = ValueNotifier<Offset?>(null); final _textSelectionAnchor = ValueNotifier<Offset?>(null);
OverlayEntry? _imageFormatBarOverlayEntry; final _imageFormatBarOverlayController = OverlayPortalController();
final _imageSelectionAnchor = ValueNotifier<Offset?>(null); final _imageSelectionAnchor = ValueNotifier<Offset?>(null);
final _overlayController = MagnifierAndToolbarController(); // TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
final _overlayController = MagnifierAndToolbarController() //
..screenPadding = const EdgeInsets.all(20.0);
late final SuperEditorIosControlsController _iosControlsController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Fix for <p> tags cutting off text // Fix for <p> tags cutting off text
var markdown = widget.value; var markdown = widget.value;
markdown = markdown.replaceAll('<p/>', '\n'); markdown = markdown.replaceAll('<p/>', '\n');
@ -60,64 +70,38 @@ class _ExampleEditorState extends State<ExampleEditor> {
markdown = markdown.replaceAll('</p>', ''); markdown = markdown.replaceAll('</p>', '');
markdown = markdown.replaceAll('</div>', ''); markdown = markdown.replaceAll('</div>', '');
// _doc = createInitialDocument()..addListener(_onDocumentChange);
_doc = deserializeMarkdownToDocument(markdown) _doc = deserializeMarkdownToDocument(markdown)
..addListener(_hideOrShowToolbar) ..addListener(_onDocumentChange);
..addListener(_onChanged); _composer = MutableDocumentComposer();
_composer.selectionNotifier.addListener(_hideOrShowToolbar);
_docEditor = DocumentEditor(document: _doc as MutableDocument); _docEditor =
_composer = DocumentComposer(); createDefaultDocumentEditor(document: _doc, composer: _composer);
_composer!.selectionNotifier.addListener(_hideOrShowToolbar);
_docOps = CommonEditorOperations( _docOps = CommonEditorOperations(
editor: _docEditor!, editor: _docEditor,
composer: _composer!, document: _doc,
composer: _composer,
documentLayoutResolver: () => documentLayoutResolver: () =>
_docLayoutKey.currentState as DocumentLayout, _docLayoutKey.currentState as DocumentLayout,
); );
_editorFocusNode = FocusNode(); _editorFocusNode = FocusNode();
_scrollController = ScrollController()..addListener(_hideOrShowToolbar); _scrollController = ScrollController()..addListener(_hideOrShowToolbar);
}
@override _iosControlsController = SuperEditorIosControlsController();
void didUpdateWidget(ExampleEditor oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.value != oldWidget.value) {
_setValue(widget.value);
}
}
void _setValue(String value) {
_doc.removeListener(_hideOrShowToolbar);
_doc.removeListener(_onChanged);
_doc = deserializeMarkdownToDocument(value)
..addListener(_hideOrShowToolbar)
..addListener(_onChanged);
_docEditor = DocumentEditor(document: _doc as MutableDocument);
_editorFocusNode = FocusNode();
} }
@override @override
void dispose() { void dispose() {
if (_textFormatBarOverlayEntry != null) { _iosControlsController.dispose();
_textFormatBarOverlayEntry!.remove(); _scrollController.dispose();
} _editorFocusNode.dispose();
_composer.dispose();
_doc.removeListener(_hideOrShowToolbar);
_doc.removeListener(_onChanged);
_scrollController!.removeListener(_hideOrShowToolbar);
_composer!.removeListener(_hideOrShowToolbar);
_scrollController!.dispose();
_editorFocusNode!.dispose();
_composer!.dispose();
super.dispose(); super.dispose();
} }
void _onChanged() { void _onDocumentChange(_) {
if (widget.onChanged != null) { _hideOrShowToolbar();
final value = serializeDocumentToMarkdown(_docEditor!.document); _docChangeSignal.notifyListeners();
widget.onChanged!(value);
}
} }
void _hideOrShowToolbar() { void _hideOrShowToolbar() {
@ -127,7 +111,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
return; return;
} }
final selection = _composer!.selection; final selection = _composer.selection;
if (selection == null) { if (selection == null) {
// Nothing is selected. We don't want to show a toolbar // Nothing is selected. We don't want to show a toolbar
// in this case. // in this case.
@ -155,6 +139,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
final selectedNode = _doc.getNodeById(selection.extent.nodeId); final selectedNode = _doc.getNodeById(selection.extent.nodeId);
if (selectedNode is ImageNode) { if (selectedNode is ImageNode) {
//appLog.fine("Showing image toolbar");
// Show the editor's toolbar for image sizing. // Show the editor's toolbar for image sizing.
_showImageToolbar(); _showImageToolbar();
_hideEditorToolbar(); _hideEditorToolbar();
@ -178,36 +163,16 @@ class _ExampleEditorState extends State<ExampleEditor> {
} }
void _showEditorToolbar() { void _showEditorToolbar() {
if (_textFormatBarOverlayEntry == null) { _textFormatBarOverlayController.show();
// 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) {
return EditorToolbar(
anchor: _textSelectionAnchor,
editorFocusNode: _editorFocusNode,
editor: _docEditor,
composer: _composer,
closeToolbar: _hideEditorToolbar,
);
});
// Display the toolbar in the application overlay.
final overlay = Overlay.of(context);
overlay.insert(_textFormatBarOverlayEntry!);
}
// Schedule a callback after this frame to locate the selection // Schedule a callback after this frame to locate the selection
// bounds on the screen and display the toolbar near the selected // bounds on the screen and display the toolbar near the selected
// text. // text.
// TODO: switch this to use a Leader and Follower
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (_textFormatBarOverlayEntry == null) {
return;
}
final docBoundingBox = (_docLayoutKey.currentState as DocumentLayout) final docBoundingBox = (_docLayoutKey.currentState as DocumentLayout)
.getRectForSelection( .getRectForSelection(
_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(
@ -224,22 +189,14 @@ class _ExampleEditorState extends State<ExampleEditor> {
// the bar doesn't momentarily "flash" at its old anchor position. // the bar doesn't momentarily "flash" at its old anchor position.
_textSelectionAnchor.value = null; _textSelectionAnchor.value = null;
if (_textFormatBarOverlayEntry != null) { _textFormatBarOverlayController.hide();
// Remove the toolbar overlay and null-out the entry.
// We null out the entry because we can't query whether
// or not the entry exists in the overlay, so in our
// case, null implies the entry is not in the overlay,
// and non-null implies the entry is in the overlay.
_textFormatBarOverlayEntry!.remove();
_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 {
@ -267,63 +224,41 @@ class _ExampleEditorState extends State<ExampleEditor> {
case TargetPlatform.macOS: case TargetPlatform.macOS:
case TargetPlatform.windows: case TargetPlatform.windows:
return TextInputSource.ime; return TextInputSource.ime;
// return DocumentInputSource.keyboard;
} }
} }
void _cut() { void _cut() {
_docOps.cut(); _docOps.cut();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar(); _overlayController.hideToolbar();
_iosControlsController.hideToolbar();
} }
void _copy() { void _copy() {
_docOps.copy(); _docOps.copy();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar(); _overlayController.hideToolbar();
_iosControlsController.hideToolbar();
} }
void _paste() { void _paste() {
_docOps.paste(); _docOps.paste();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar(); _overlayController.hideToolbar();
_iosControlsController.hideToolbar();
} }
void _selectAll() => _docOps.selectAll(); void _selectAll() => _docOps.selectAll();
void _showImageToolbar() { void _showImageToolbar() {
if (_imageFormatBarOverlayEntry == null) {
// Create an overlay entry to build the image toolbar.
_imageFormatBarOverlayEntry ??= OverlayEntry(builder: (context) {
return ImageFormatToolbar(
anchor: _imageSelectionAnchor,
composer: _composer,
setWidth: (nodeId, width) {
final node = _doc.getNodeById(nodeId)!;
final currentStyles =
SingleColumnLayoutComponentStyles.fromMetadata(node);
SingleColumnLayoutComponentStyles(
width: width,
padding: currentStyles.padding,
).applyTo(node);
},
closeToolbar: _hideImageToolbar,
);
});
// Display the toolbar in the application overlay.
final overlay = Overlay.of(context);
overlay.insert(_imageFormatBarOverlayEntry!);
}
// Schedule a callback after this frame to locate the selection // Schedule a callback after this frame to locate the selection
// bounds on the screen and display the toolbar near the selected // bounds on the screen and display the toolbar near the selected
// text. // text.
// TODO: switch to a Leader and Follower for this
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (_imageFormatBarOverlayEntry == null) {
return;
}
final docBoundingBox = (_docLayoutKey.currentState as DocumentLayout) final docBoundingBox = (_docLayoutKey.currentState as DocumentLayout)
.getRectForSelection( .getRectForSelection(
_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(
@ -333,6 +268,8 @@ class _ExampleEditorState extends State<ExampleEditor> {
_imageSelectionAnchor.value = overlayBoundingBox.center; _imageSelectionAnchor.value = overlayBoundingBox.center;
}); });
_imageFormatBarOverlayController.show();
} }
void _hideImageToolbar() { void _hideImageToolbar() {
@ -340,49 +277,67 @@ class _ExampleEditorState extends State<ExampleEditor> {
// it doesn't momentarily "flash" at its old anchor position. // it doesn't momentarily "flash" at its old anchor position.
_imageSelectionAnchor.value = null; _imageSelectionAnchor.value = null;
if (_imageFormatBarOverlayEntry != null) { _imageFormatBarOverlayController.hide();
// Remove the image toolbar overlay and null-out the entry.
// We null out the entry because we can't query whether
// or not the entry exists in the overlay, so in our
// case, null implies the entry is not in the overlay,
// and non-null implies the entry is in the overlay.
_imageFormatBarOverlayEntry!.remove();
_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 ValueListenableBuilder(
valueListenable: _brightness,
builder: (context, brightness, child) {
return Theme( return Theme(
data: ThemeData(brightness: Brightness.light), data: ThemeData(brightness: brightness),
child: Builder(builder: (themedContext) { child: child!,
);
},
child: Builder(
// This builder captures the new theme // This builder captures the new theme
return Stack( builder: (themedContext) {
return OverlayPortal(
controller: _textFormatBarOverlayController,
overlayChildBuilder: _buildFloatingToolbar,
child: OverlayPortal(
controller: _imageFormatBarOverlayController,
overlayChildBuilder: _buildImageToolbar,
child: Stack(
children: [ children: [
Column( Column(
children: [ children: [
Expanded( Expanded(
child: _buildEditor(themedContext), child: _buildEditor(themedContext),
), ),
if (_isMobile) _buildMountedToolbar(), if (_isMobile) //
_buildMountedToolbar(),
], ],
), ),
/*
Align( Align(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
child: ListenableBuilder(
listenable: _composer.selectionNotifier,
builder: (context, child) {
return Padding(
padding: EdgeInsets.only(
bottom: _isMobile && _composer.selection != null
? 48
: 0),
child: child,
);
},
child: _buildCornerFabs(), child: _buildCornerFabs(),
), ),
*/ ),
], ],
),
),
); );
}), },
),
); );
} }
/*
Widget _buildCornerFabs() { Widget _buildCornerFabs() {
return Padding( return Padding(
padding: const EdgeInsets.only(right: 16, bottom: 16), padding: const EdgeInsets.only(right: 16, bottom: 16),
@ -397,21 +352,21 @@ class _ExampleEditorState extends State<ExampleEditor> {
), ),
); );
} }
*/
/*
Widget _buildDebugVisualsToggle() { Widget _buildDebugVisualsToggle() {
return FloatingActionButton( return FloatingActionButton(
backgroundColor: backgroundColor: _brightness.value == Brightness.light
_brightness == Brightness.light ? _darkBackground : _lightBackground, ? _darkBackground
foregroundColor: : _lightBackground,
_brightness == Brightness.light ? _lightBackground : _darkBackground, foregroundColor: _brightness.value == Brightness.light
? _lightBackground
: _darkBackground,
elevation: 5, elevation: 5,
onPressed: () { onPressed: () {
setState(() { setState(() {
_debugConfig = _debugConfig != null _debugConfig = _debugConfig != null
? null ? null
: SuperEditorDebugVisualsConfig( : const SuperEditorDebugVisualsConfig(
showFocus: true, showFocus: true,
showImeConnection: true, showImeConnection: true,
); );
@ -425,19 +380,19 @@ class _ExampleEditorState extends State<ExampleEditor> {
Widget _buildLightAndDarkModeToggle() { Widget _buildLightAndDarkModeToggle() {
return FloatingActionButton( return FloatingActionButton(
backgroundColor: backgroundColor: _brightness.value == Brightness.light
_brightness == Brightness.light ? _darkBackground : _lightBackground, ? _darkBackground
foregroundColor: : _lightBackground,
_brightness == Brightness.light ? _lightBackground : _darkBackground, foregroundColor: _brightness.value == Brightness.light
? _lightBackground
: _darkBackground,
elevation: 5, elevation: 5,
onPressed: () { onPressed: () {
setState(() { _brightness.value = _brightness.value == Brightness.light
_brightness = _brightness == Brightness.light
? Brightness.dark ? Brightness.dark
: Brightness.light; : Brightness.light;
});
}, },
child: _brightness == Brightness.light child: _brightness.value == Brightness.light
? const Icon( ? const Icon(
Icons.dark_mode, Icons.dark_mode,
) )
@ -446,7 +401,6 @@ class _ExampleEditorState extends State<ExampleEditor> {
), ),
); );
} }
*/
Widget _buildEditor(BuildContext context) { Widget _buildEditor(BuildContext context) {
final isLight = Theme.of(context).brightness == Brightness.light; final isLight = Theme.of(context).brightness == Brightness.light;
@ -455,18 +409,32 @@ class _ExampleEditorState extends State<ExampleEditor> {
color: isLight ? _lightBackground : _darkBackground, color: isLight ? _lightBackground : _darkBackground,
child: SuperEditorDebugVisuals( child: SuperEditorDebugVisuals(
config: _debugConfig ?? const SuperEditorDebugVisualsConfig(), config: _debugConfig ?? const SuperEditorDebugVisualsConfig(),
child: KeyedSubtree(
key: _viewportKey,
child: SuperEditorIosControlsScope(
controller: _iosControlsController,
child: SuperEditor( child: SuperEditor(
editor: _docEditor!, editor: _docEditor,
document: _doc,
composer: _composer, composer: _composer,
focusNode: _editorFocusNode, focusNode: _editorFocusNode,
scrollController: _scrollController, scrollController: _scrollController,
documentLayoutKey: _docLayoutKey, documentLayoutKey: _docLayoutKey,
documentOverlayBuilders: [ documentOverlayBuilders: [
DefaultCaretOverlayBuilder( DefaultCaretOverlayBuilder(
CaretStyle() caretStyle: const CaretStyle().copyWith(
.copyWith(color: isLight ? Colors.black : Colors.redAccent), color: isLight ? Colors.black : Colors.redAccent),
), ),
if (defaultTargetPlatform == TargetPlatform.iOS) ...[
SuperEditorAndroidToolbarFocalPointDocumentLayerBuilder(),
SuperEditorAndroidHandlesDocumentLayerBuilder(),
], ],
if (defaultTargetPlatform == TargetPlatform.android) ...[
SuperEditorAndroidToolbarFocalPointDocumentLayerBuilder(),
SuperEditorAndroidHandlesDocumentLayerBuilder(),
],
],
selectionLayerLinks: _selectionLayerLinks,
selectionStyle: isLight selectionStyle: isLight
? defaultSelectionStyle ? defaultSelectionStyle
: SelectionStyles( : SelectionStyles(
@ -479,7 +447,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
], ],
), ),
componentBuilders: [ componentBuilders: [
TaskComponentBuilder(_docEditor!), TaskComponentBuilder(_docEditor),
...defaultComponentBuilders, ...defaultComponentBuilders,
], ],
gestureMode: _gestureMode, gestureMode: _gestureMode,
@ -487,45 +455,88 @@ class _ExampleEditorState extends State<ExampleEditor> {
keyboardActions: _inputSource == TextInputSource.ime keyboardActions: _inputSource == TextInputSource.ime
? defaultImeKeyboardActions ? defaultImeKeyboardActions
: defaultKeyboardActions, : defaultKeyboardActions,
androidToolbarBuilder: (_) => AndroidTextEditingFloatingToolbar( androidToolbarBuilder: (_) => _buildAndroidFloatingToolbar(),
overlayController: _overlayController,
),
),
),
),
);
}
Widget _buildAndroidFloatingToolbar() {
return ListenableBuilder(
listenable: _brightness,
builder: (context, _) {
return Theme(
data: ThemeData(brightness: _brightness.value),
child: AndroidTextEditingFloatingToolbar(
onCutPressed: _cut, onCutPressed: _cut,
onCopyPressed: _copy, onCopyPressed: _copy,
onPastePressed: _paste, onPastePressed: _paste,
onSelectAllPressed: _selectAll, onSelectAllPressed: _selectAll,
), ),
iOSToolbarBuilder: (_) => IOSTextEditingFloatingToolbar( );
onCutPressed: _cut, },
onCopyPressed: _copy,
onPastePressed: _paste,
focalPoint: _overlayController.toolbarTopAnchor!,
),
overlayController: _overlayController,
),
),
); );
} }
Widget _buildMountedToolbar() { Widget _buildMountedToolbar() {
return MultiListenableBuilder( return MultiListenableBuilder(
listenables: <Listenable>{ listenables: <Listenable>{
_doc, _docChangeSignal,
_composer!.selectionNotifier, _composer.selectionNotifier,
}, },
builder: (_) { builder: (_) {
final selection = _composer!.selection; final selection = _composer.selection;
if (selection == null) { if (selection == null) {
return const SizedBox(); return const SizedBox();
} }
return KeyboardEditingToolbar( return KeyboardEditingToolbar(
editor: _docEditor,
document: _doc, document: _doc,
composer: _composer!, composer: _composer,
commonOps: _docOps, commonOps: _docOps,
); );
}, },
); );
} }
Widget _buildFloatingToolbar(BuildContext context) {
return EditorToolbar(
editorViewportKey: _viewportKey,
anchor: _selectionLayerLinks.expandedSelectionBoundsLink,
editorFocusNode: _editorFocusNode,
editor: _docEditor,
document: _doc,
composer: _composer,
closeToolbar: _hideEditorToolbar,
);
}
Widget _buildImageToolbar(BuildContext context) {
return ImageFormatToolbar(
anchor: _imageSelectionAnchor,
composer: _composer,
setWidth: (nodeId, width) {
print('Applying width $width to node $nodeId');
final node = _doc.getNodeById(nodeId)!;
final currentStyles =
SingleColumnLayoutComponentStyles.fromMetadata(node);
SingleColumnLayoutComponentStyles(
width: width,
padding: currentStyles.padding,
).applyTo(node);
// TODO: schedule a presentation reflow so that the image changes size immediately (https://github.com/superlistapp/super_editor/issues/1529)
// Right now, nothing happens when pressing the button, unless we force a
// rebuild/reflow.
},
closeToolbar: _hideImageToolbar,
);
}
} }
// Makes text light, for use during dark mode styling. // Makes text light, for use during dark mode styling.
@ -533,7 +544,7 @@ final _darkModeStyles = [
StyleRule( StyleRule(
BlockSelector.all, BlockSelector.all,
(doc, docNode) { (doc, docNode) {
return <String, dynamic>{ return {
'textStyle': const TextStyle( 'textStyle': const TextStyle(
color: Color(0xFFCCCCCC), color: Color(0xFFCCCCCC),
), ),
@ -543,7 +554,7 @@ final _darkModeStyles = [
StyleRule( StyleRule(
const BlockSelector('header1'), const BlockSelector('header1'),
(doc, docNode) { (doc, docNode) {
return <String, dynamic>{ return {
'textStyle': const TextStyle( 'textStyle': const TextStyle(
color: Color(0xFF888888), color: Color(0xFF888888),
), ),
@ -553,7 +564,7 @@ final _darkModeStyles = [
StyleRule( StyleRule(
const BlockSelector('header2'), const BlockSelector('header2'),
(doc, docNode) { (doc, docNode) {
return <String, dynamic>{ return {
'textStyle': const TextStyle( 'textStyle': const TextStyle(
color: Color(0xFF888888), color: Color(0xFF888888),
), ),

View File

@ -1,6 +1,10 @@
import 'dart:math'; import 'dart:math';
//import 'package:example/logging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
//import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:follow_the_leader/follow_the_leader.dart';
import 'package:overlord/follow_the_leader.dart';
import 'package:super_editor/super_editor.dart'; import 'package:super_editor/super_editor.dart';
/// Small toolbar that is intended to display near some selected /// Small toolbar that is intended to display near some selected
@ -13,33 +17,40 @@ import 'package:super_editor/super_editor.dart';
class EditorToolbar extends StatefulWidget { class EditorToolbar extends StatefulWidget {
const EditorToolbar({ const EditorToolbar({
Key? key, Key? key,
required this.anchor, required this.editorViewportKey,
required this.editorFocusNode, required this.editorFocusNode,
required this.editor, required this.editor,
required this.document,
required this.composer, required this.composer,
required this.anchor,
required this.closeToolbar, required this.closeToolbar,
}) : super(key: key); }) : super(key: key);
/// [EditorToolbar] displays itself horizontally centered and /// [GlobalKey] that should be attached to a widget that wraps the viewport
/// slightly above the given [anchor] value. /// area, which keeps the toolbar from appearing outside of the editor area.
final GlobalKey editorViewportKey;
/// A [LeaderLink] that should be attached to the boundary of the toolbar
/// focal area, such as wrapped around the user's selection area.
/// ///
/// [anchor] is a [ValueNotifier] so that [EditorToolbar] can /// The toolbar is positioned relative to this anchor link.
/// reposition itself as the [Offset] value changes. final LeaderLink anchor;
final ValueNotifier<Offset?> anchor;
/// The [FocusNode] attached to the editor to which this toolbar applies. /// The [FocusNode] attached to the editor to which this toolbar applies.
final FocusNode? editorFocusNode; 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
/// to apply styles to text. /// to apply styles to text.
final DocumentEditor? editor; final Editor? editor;
final Document document;
/// The [composer] provides access to the user's current /// The [composer] provides access to the user's current
/// selection within the document, which dictates the /// selection within the document, which dictates the
/// content that is altered by the toolbar's options. /// content that is altered by the toolbar's options.
final DocumentComposer? composer; final DocumentComposer composer;
/// Delegate that instructs the owner of this [EditorToolbar] /// Delegate that instructs the owner of this [EditorToolbar]
/// to close the toolbar, such as after submitting a URL /// to close the toolbar, such as after submitting a URL
@ -47,41 +58,62 @@ class EditorToolbar extends StatefulWidget {
final VoidCallback closeToolbar; final VoidCallback closeToolbar;
@override @override
_EditorToolbarState createState() => _EditorToolbarState(); State<EditorToolbar> createState() => _EditorToolbarState();
} }
class _EditorToolbarState extends State<EditorToolbar> { class _EditorToolbarState extends State<EditorToolbar> {
late final FollowerAligner _toolbarAligner;
late FollowerBoundary _screenBoundary;
bool _showUrlField = false; bool _showUrlField = false;
FocusNode? _urlFocusNode; late FocusNode _popoverFocusNode;
AttributedTextEditingController? _urlController; late FocusNode _urlFocusNode;
ImeAttributedTextEditingController? _urlController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_toolbarAligner = CupertinoPopoverToolbarAligner(widget.editorViewportKey);
_popoverFocusNode = FocusNode();
_urlFocusNode = FocusNode(); _urlFocusNode = FocusNode();
_urlController = SingleLineAttributedTextEditingController(_applyLink); _urlController = ImeAttributedTextEditingController(
controller: SingleLineAttributedTextEditingController(_applyLink)) //
..onPerformActionPressed = _onPerformAction
..text = AttributedText('https://');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_screenBoundary = WidgetFollowerBoundary(
boundaryKey: widget.editorViewportKey,
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
);
} }
@override @override
void dispose() { void dispose() {
_urlFocusNode!.dispose(); _urlFocusNode.dispose();
_urlController!.dispose(); _urlController!.dispose();
_popoverFocusNode.dispose();
super.dispose(); super.dispose();
} }
/*
/// Returns true if the currently selected text node is capable of being /// Returns true if the currently selected text node is capable of being
/// transformed into a different type text node, returns false if /// transformed into a different type text node, returns false if
/// multiple nodes are selected, no node is selected, or the selected /// multiple nodes are selected, no node is selected, or the selected
/// node is not a standard text block. /// node is not a standard text block.
bool _isConvertibleNode() { bool _isConvertibleNode() {
final selection = widget.composer.selection; final selection = widget.composer.selection!;
if (selection.base.nodeId != selection.extent.nodeId) { if (selection.base.nodeId != selection.extent.nodeId) {
return false; return false;
} }
final selectedNode = final selectedNode = widget.document.getNodeById(selection.extent.nodeId);
widget.editor.document.getNodeById(selection.extent.nodeId);
return selectedNode is ParagraphNode || selectedNode is ListItemNode; return selectedNode is ParagraphNode || selectedNode is ListItemNode;
} }
@ -89,10 +121,10 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// ///
/// 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.
_TextType _getCurrentTextType() { _TextType _getCurrentTextType() {
final selectedNode = widget.editor.document final selectedNode =
.getNodeById(widget.composer.selection.extent.nodeId); widget.document.getNodeById(widget.composer.selection!.extent.nodeId);
if (selectedNode is ParagraphNode) { if (selectedNode is ParagraphNode) {
final dynamic type = selectedNode.getMetadataValue('blockType'); final type = selectedNode.getMetadataValue('blockType');
if (type == header1Attribution) { if (type == header1Attribution) {
return _TextType.header1; return _TextType.header1;
@ -118,10 +150,10 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// ///
/// 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 =
.getNodeById(widget.composer.selection.extent.nodeId); widget.document.getNodeById(widget.composer.selection!.extent.nodeId);
if (selectedNode is ParagraphNode) { if (selectedNode is ParagraphNode) {
final dynamic align = selectedNode.getMetadataValue('textAlign'); final align = selectedNode.getMetadataValue('textAlign');
switch (align) { switch (align) {
case 'left': case 'left':
return TextAlign.left; return TextAlign.left;
@ -143,13 +175,12 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// Returns true if a single text node is selected and that text node /// Returns true if a single text node is selected and that text node
/// is capable of respecting alignment, returns false otherwise. /// is capable of respecting alignment, returns false otherwise.
bool _isTextAlignable() { bool _isTextAlignable() {
final selection = widget.composer.selection; final selection = widget.composer.selection!;
if (selection.base.nodeId != selection.extent.nodeId) { if (selection.base.nodeId != selection.extent.nodeId) {
return false; return false;
} }
final selectedNode = final selectedNode = widget.document.getNodeById(selection.extent.nodeId);
widget.editor.document.getNodeById(selection.extent.nodeId);
return selectedNode is ParagraphNode; return selectedNode is ParagraphNode;
} }
@ -158,7 +189,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// ///
/// For example: convert a paragraph to a blockquote, or a header /// For example: convert a paragraph to a blockquote, or a header
/// to a list item. /// to a list item.
void _convertTextToNewType(_TextType newType) { void _convertTextToNewType(_TextType? newType) {
final existingTextType = _getCurrentTextType(); final existingTextType = _getCurrentTextType();
if (existingTextType == newType) { if (existingTextType == newType) {
@ -167,53 +198,53 @@ class _EditorToolbarState extends State<EditorToolbar> {
} }
if (_isListItem(existingTextType) && _isListItem(newType)) { if (_isListItem(existingTextType) && _isListItem(newType)) {
widget.editor.executeCommand( widget.editor!.execute([
ChangeListItemTypeCommand( ChangeListItemTypeRequest(
nodeId: widget.composer.selection.extent.nodeId, nodeId: widget.composer.selection!.extent.nodeId,
newType: newType == _TextType.orderedListItem newType: newType == _TextType.orderedListItem
? ListItemType.ordered ? ListItemType.ordered
: ListItemType.unordered, : ListItemType.unordered,
), ),
); ]);
} else if (_isListItem(existingTextType) && !_isListItem(newType)) { } else if (_isListItem(existingTextType) && !_isListItem(newType)) {
widget.editor.executeCommand( widget.editor!.execute([
ConvertListItemToParagraphCommand( ConvertListItemToParagraphRequest(
nodeId: widget.composer.selection.extent.nodeId, nodeId: widget.composer.selection!.extent.nodeId,
paragraphMetadata: <String, dynamic>{ paragraphMetadata: {
'blockType': _getBlockTypeAttribution(newType), 'blockType': _getBlockTypeAttribution(newType),
}, },
), ),
); ]);
} else if (!_isListItem(existingTextType) && _isListItem(newType)) { } else if (!_isListItem(existingTextType) && _isListItem(newType)) {
widget.editor.executeCommand( widget.editor!.execute([
ConvertParagraphToListItemCommand( ConvertParagraphToListItemRequest(
nodeId: widget.composer.selection.extent.nodeId, nodeId: widget.composer.selection!.extent.nodeId,
type: newType == _TextType.orderedListItem type: newType == _TextType.orderedListItem
? ListItemType.ordered ? ListItemType.ordered
: ListItemType.unordered, : ListItemType.unordered,
), ),
); ]);
} else { } else {
// Apply a new block type to an existing paragraph node. // Apply a new block type to an existing paragraph node.
final existingNode = widget.editor.document widget.editor!.execute([
.getNodeById(widget.composer.selection.extent.nodeId) ChangeParagraphBlockTypeRequest(
as ParagraphNode; nodeId: widget.composer.selection!.extent.nodeId,
existingNode.putMetadataValue( blockType: _getBlockTypeAttribution(newType),
'blockType', _getBlockTypeAttribution(newType)); ),
]);
} }
} }
/// Returns true if the given [_TextType] represents an /// Returns true if the given [_TextType] represents an
/// ordered or unordered list item, returns false otherwise. /// ordered or unordered list item, returns false otherwise.
bool _isListItem(_TextType type) { bool _isListItem(_TextType? type) {
return type == _TextType.orderedListItem || return type == _TextType.orderedListItem ||
type == _TextType.unorderedListItem; type == _TextType.unorderedListItem;
} }
/// Returns the text [Attribution] associated with the given /// Returns the text [Attribution] associated with the given
/// [_TextType], e.g., [_TextType.header1] -> [header1Attribution]. /// [_TextType], e.g., [_TextType.header1] -> [header1Attribution].
Attribution _getBlockTypeAttribution(_TextType newType) { Attribution? _getBlockTypeAttribution(_TextType? newType) {
switch (newType) { switch (newType) {
case _TextType.header1: case _TextType.header1:
return header1Attribution; return header1Attribution;
@ -228,36 +259,35 @@ class _EditorToolbarState extends State<EditorToolbar> {
return null; return null;
} }
} }
*/
/// Toggles bold styling for the current selected text. /// Toggles bold styling for the current selected text.
void _toggleBold() { void _toggleBold() {
widget.editor!.executeCommand( widget.editor!.execute([
ToggleTextAttributionsCommand( ToggleTextAttributionsRequest(
documentSelection: widget.composer!.selection!, documentRange: widget.composer.selection!,
attributions: {boldAttribution}, attributions: {boldAttribution},
), ),
); ]);
} }
/// Toggles italic styling for the current selected text. /// Toggles italic styling for the current selected text.
void _toggleItalics() { void _toggleItalics() {
widget.editor!.executeCommand( widget.editor!.execute([
ToggleTextAttributionsCommand( ToggleTextAttributionsRequest(
documentSelection: widget.composer!.selection!, documentRange: widget.composer.selection!,
attributions: {italicsAttribution}, attributions: {italicsAttribution},
), ),
); ]);
} }
/// Toggles strikethrough styling for the current selected text. /// Toggles strikethrough styling for the current selected text.
void _toggleStrikethrough() { void _toggleStrikethrough() {
widget.editor!.executeCommand( widget.editor!.execute([
ToggleTextAttributionsCommand( ToggleTextAttributionsRequest(
documentSelection: widget.composer!.selection!, documentRange: widget.composer.selection!,
attributions: {strikethroughAttribution}, attributions: {strikethroughAttribution},
), ),
); ]);
} }
/// Returns true if the current text selection includes part /// Returns true if the current text selection includes part
@ -276,16 +306,15 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// Returns any link-based [AttributionSpan]s that appear partially /// Returns any link-based [AttributionSpan]s that appear partially
/// or wholly within the current text selection. /// or wholly within the current text selection.
Set<AttributionSpan> _getSelectedLinkSpans() { Set<AttributionSpan> _getSelectedLinkSpans() {
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;
final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final extentOffset = (selection.extent.nodePosition as TextPosition).offset;
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(selectionStart, selectionEnd - 1);
SpanRange(start: selectionStart, end: selectionEnd - 1);
final textNode = widget.editor!.document final textNode =
.getNodeById(selection.extent.nodeId) as TextNode; widget.document.getNodeById(selection.extent.nodeId) as TextNode;
final text = textNode.text; final text = textNode.text;
final overlappingLinkAttributions = text.getAttributionSpansInRange( final overlappingLinkAttributions = text.getAttributionSpansInRange(
@ -300,16 +329,15 @@ class _EditorToolbarState extends State<EditorToolbar> {
/// Takes appropriate action when the toolbar's link button is /// Takes appropriate action when the toolbar's link button is
/// pressed. /// pressed.
void _onLinkPressed() { void _onLinkPressed() {
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;
final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final extentOffset = (selection.extent.nodePosition as TextPosition).offset;
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(selectionStart, selectionEnd - 1);
SpanRange(start: selectionStart, end: selectionEnd - 1);
final textNode = widget.editor!.document final textNode =
.getNodeById(selection.extent.nodeId) as TextNode; widget.document.getNodeById(selection.extent.nodeId) as TextNode;
final text = textNode.text; final text = textNode.text;
final overlappingLinkAttributions = text.getAttributionSpansInRange( final overlappingLinkAttributions = text.getAttributionSpansInRange(
@ -341,15 +369,14 @@ class _EditorToolbarState extends State<EditorToolbar> {
// the entire link attribution. // the entire link attribution.
text.removeAttribution( text.removeAttribution(
overlappingLinkSpan.attribution, overlappingLinkSpan.attribution,
SpanRange( SpanRange(overlappingLinkSpan.start, overlappingLinkSpan.end),
start: overlappingLinkSpan.start, end: overlappingLinkSpan.end),
); );
} }
} else { } else {
// There are no other links in the selection. Show the URL text field. // There are no other links in the selection. Show the URL text field.
setState(() { setState(() {
_showUrlField = true; _showUrlField = true;
_urlFocusNode!.requestFocus(); _urlFocusNode.requestFocus();
}); });
} }
} }
@ -359,7 +386,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
void _applyLink() { void _applyLink() {
final url = _urlController!.text.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;
final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final extentOffset = (selection.extent.nodePosition as TextPosition).offset;
final selectionStart = min(baseOffset, extentOffset); final selectionStart = min(baseOffset, extentOffset);
@ -367,8 +394,8 @@ class _EditorToolbarState extends State<EditorToolbar> {
final selectionRange = final selectionRange =
TextRange(start: selectionStart, end: selectionEnd - 1); TextRange(start: selectionStart, end: selectionEnd - 1);
final textNode = widget.editor!.document final textNode =
.getNodeById(selection.extent.nodeId) as TextNode; widget.document.getNodeById(selection.extent.nodeId) as TextNode;
final text = textNode.text; final text = textNode.text;
final trimmedRange = _trimTextRangeWhitespace(text, selectionRange); final trimmedRange = _trimTextRangeWhitespace(text, selectionRange);
@ -383,8 +410,8 @@ class _EditorToolbarState extends State<EditorToolbar> {
_urlController!.clear(); _urlController!.clear();
setState(() { setState(() {
_showUrlField = false; _showUrlField = false;
_urlFocusNode! _urlFocusNode.unfocus(
.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild); disposition: UnfocusDisposition.previouslyFocusedChild);
widget.closeToolbar(); widget.closeToolbar();
}); });
} }
@ -403,17 +430,16 @@ class _EditorToolbarState extends State<EditorToolbar> {
endOffset -= 1; endOffset -= 1;
} }
return SpanRange(start: startOffset, end: endOffset); return SpanRange(startOffset, endOffset);
} }
/*
/// Changes the alignment of the current selected text node /// Changes the alignment of the current selected text node
/// to reflect [newAlignment]. /// to reflect [newAlignment].
void _changeAlignment(TextAlign newAlignment) { void _changeAlignment(TextAlign? newAlignment) {
if (newAlignment == null) { if (newAlignment == null) {
return; return;
} }
String newAlignmentValue; String? newAlignmentValue;
switch (newAlignment) { switch (newAlignment) {
case TextAlign.left: case TextAlign.left:
case TextAlign.start: case TextAlign.start:
@ -431,8 +457,8 @@ class _EditorToolbarState extends State<EditorToolbar> {
break; break;
} }
final selectedNode = widget.editor.document final selectedNode = widget.document
.getNodeById(widget.composer.selection.extent.nodeId) as ParagraphNode; .getNodeById(widget.composer.selection!.extent.nodeId) as ParagraphNode;
selectedNode.putMetadataValue('textAlign', newAlignmentValue); selectedNode.putMetadataValue('textAlign', newAlignmentValue);
} }
@ -441,66 +467,74 @@ class _EditorToolbarState extends State<EditorToolbar> {
String _getTextTypeName(_TextType textType) { String _getTextTypeName(_TextType textType) {
switch (textType) { switch (textType) {
case _TextType.header1: case _TextType.header1:
// return AppLocalizations.of(context)!.labelHeader1;
return 'Header 1'; return 'Header 1';
case _TextType.header2: case _TextType.header2:
// return AppLocalizations.of(context)!.labelHeader2;
return 'Header 2'; return 'Header 2';
case _TextType.header3: case _TextType.header3:
// return AppLocalizations.of(context)!.labelHeader3;
return 'Header 3'; return 'Header 3';
case _TextType.paragraph: case _TextType.paragraph:
// return AppLocalizations.of(context)!.labelParagraph;
return 'Paragraph'; return 'Paragraph';
case _TextType.blockquote: case _TextType.blockquote:
// return AppLocalizations.of(context)!.labelBlockquote;
return 'Blockquote'; return 'Blockquote';
case _TextType.orderedListItem: case _TextType.orderedListItem:
// return AppLocalizations.of(context)!.labelOrderedListItem;
return 'Ordered List Item'; return 'Ordered List Item';
case _TextType.unorderedListItem: case _TextType.unorderedListItem:
// return AppLocalizations.of(context)!.labelUnorderedListItem;
return 'Unordered List Item'; return 'Unordered List Item';
} }
return '';
} }
*/
void _onPerformAction(TextInputAction action) {
if (action == TextInputAction.done) {
_applyLink();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack( return BuildInOrder(
children: [ children: [
// Conditionally display the URL text field below FollowerFadeOutBeyondBoundary(
// the standard toolbar. link: widget.anchor,
if (_showUrlField) boundary: _screenBoundary,
Positioned( child: Follower.withAligner(
left: widget.anchor.value!.dx, link: widget.anchor,
top: widget.anchor.value!.dy, aligner: _toolbarAligner,
child: FractionalTranslation( boundary: _screenBoundary,
translation: const Offset(-0.5, 0.0), showWhenUnlinked: false,
child: _buildUrlField(), child: _buildToolbars(),
),
),
_PositionedToolbar(
anchor: widget.anchor,
composer: widget.composer,
child: ValueListenableBuilder<DocumentSelection?>(
valueListenable: widget.composer!.selectionNotifier,
builder: (context, selection, child) {
if (selection == null) {
return const SizedBox();
}
if (selection.extent.nodePosition is! TextPosition) {
// The user selected non-text content. This toolbar is probably
// about to disappear. Until then, build nothing, because the
// toolbar needs to inspect selected text to build correctly.
return const SizedBox();
}
return _buildToolbar();
},
), ),
), ),
], ],
); );
} }
Widget _buildToolbars() {
return SuperEditorPopover(
popoverFocusNode: _popoverFocusNode,
editorFocusNode: widget.editorFocusNode,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildToolbar(),
if (_showUrlField) ...[
const SizedBox(height: 8),
_buildUrlField(),
],
],
),
);
}
Widget _buildToolbar() { Widget _buildToolbar() {
return Material( return IntrinsicWidth(
child: Material(
shape: const StadiumBorder(), shape: const StadiumBorder(),
elevation: 5, elevation: 5,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
@ -509,14 +543,12 @@ 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()) ...[
Tooltip( Tooltip(
message: 'Text Block Type', //message: AppLocalizations.of(context)!.labelTextBlockType,
message: 'Block Type',
child: DropdownButton<_TextType>( child: DropdownButton<_TextType>(
value: _getCurrentTextType(), value: _getCurrentTextType(),
items: _TextType.values items: _TextType.values
@ -529,10 +561,8 @@ class _EditorToolbarState extends State<EditorToolbar> {
)) ))
.toList(), .toList(),
icon: const Icon(Icons.arrow_drop_down), icon: const Icon(Icons.arrow_drop_down),
style: TextStyle( style: const TextStyle(
color: Theme.of(context).brightness == Brightness.dark color: Colors.black,
? Colors.white
: Colors.black,
fontSize: 12, fontSize: 12,
), ),
underline: const SizedBox(), underline: const SizedBox(),
@ -543,12 +573,12 @@ class _EditorToolbarState extends State<EditorToolbar> {
), ),
_buildVerticalDivider(), _buildVerticalDivider(),
], ],
*/
Center( Center(
child: IconButton( child: IconButton(
onPressed: _toggleBold, onPressed: _toggleBold,
icon: const Icon(Icons.format_bold), icon: const Icon(Icons.format_bold),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.Bold,
tooltip: 'Bold', tooltip: 'Bold',
), ),
), ),
@ -557,6 +587,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
onPressed: _toggleItalics, onPressed: _toggleItalics,
icon: const Icon(Icons.format_italic), icon: const Icon(Icons.format_italic),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelItalics,
tooltip: 'Italics', tooltip: 'Italics',
), ),
), ),
@ -565,26 +596,29 @@ class _EditorToolbarState extends State<EditorToolbar> {
onPressed: _toggleStrikethrough, onPressed: _toggleStrikethrough,
icon: const Icon(Icons.strikethrough_s), icon: const Icon(Icons.strikethrough_s),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelStrikethrough,
tooltip: 'Strikethrough', tooltip: 'Strikethrough',
), ),
), ),
Center( Center(
child: IconButton( child: IconButton(
onPressed: _areMultipleLinksSelected() ? null : _onLinkPressed, onPressed:
_areMultipleLinksSelected() ? null : _onLinkPressed,
icon: const Icon(Icons.link), icon: const Icon(Icons.link),
color: _isSingleLinkSelected() color: _isSingleLinkSelected()
? const Color(0xFF007AFF) ? const Color(0xFF007AFF)
: IconTheme.of(context).color, : IconTheme.of(context).color,
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelLink,
tooltip: 'Link', tooltip: 'Link',
), ),
), ),
// Only display alignment controls if the currently selected text // Only display alignment controls if the currently selected text
// node respects alignment. List items, for example, do not. // node respects alignment. List items, for example, do not.
/*
if (_isTextAlignable()) ...[ if (_isTextAlignable()) ...[
_buildVerticalDivider(), _buildVerticalDivider(),
Tooltip( Tooltip(
//message: AppLocalizations.of(context)!.labelTextAlignment,
message: 'Text Alignment', message: 'Text Alignment',
child: DropdownButton<TextAlign>( child: DropdownButton<TextAlign>(
value: _getCurrentTextAlignment(), value: _getCurrentTextAlignment(),
@ -620,13 +654,14 @@ class _EditorToolbarState extends State<EditorToolbar> {
onPressed: () {}, onPressed: () {},
icon: const Icon(Icons.more_vert), icon: const Icon(Icons.more_vert),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelMoreOptions,
tooltip: 'More Options', tooltip: 'More Options',
), ),
), ),
*/
], ],
), ),
), ),
),
); );
} }
@ -642,9 +677,9 @@ class _EditorToolbarState extends State<EditorToolbar> {
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: FocusWithCustomParent( child: Focus(
focusNode: _urlFocusNode, focusNode: _urlFocusNode,
parentFocusNode: widget.editorFocusNode, parentNode: _popoverFocusNode,
// We use a SuperTextField instead of a TextField because TextField // We use a SuperTextField instead of a TextField because TextField
// automatically re-parents its FocusNode, which causes #609. Flutter // automatically re-parents its FocusNode, which causes #609. Flutter
// #106923 tracks the TextField issue. // #106923 tracks the TextField issue.
@ -656,19 +691,17 @@ class _EditorToolbarState extends State<EditorToolbar> {
inputSource: TextInputSource.ime, inputSource: TextInputSource.ime,
hintBehavior: HintBehavior.displayHintUntilTextEntered, hintBehavior: HintBehavior.displayHintUntilTextEntered,
hintBuilder: (context) { hintBuilder: (context) {
return Text( return const Text(
'Enter a url...', 'enter a url...',
style: const TextStyle( style: TextStyle(
color: Colors.grey, color: Colors.grey,
fontSize: 16, fontSize: 16,
), ),
); );
}, },
textStyleBuilder: (_) { textStyleBuilder: (_) {
return TextStyle( return const TextStyle(
color: Theme.of(context).brightness == Brightness.dark color: Colors.black,
? Colors.white
: Colors.black,
fontSize: 16, fontSize: 16,
); );
}, },
@ -682,7 +715,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: () { onPressed: () {
setState(() { setState(() {
_urlFocusNode!.unfocus(); _urlFocusNode.unfocus();
_showUrlField = false; _showUrlField = false;
_urlController!.clear(); _urlController!.clear();
}); });
@ -694,7 +727,6 @@ class _EditorToolbarState extends State<EditorToolbar> {
); );
} }
/*
Widget _buildVerticalDivider() { Widget _buildVerticalDivider() {
return Container( return Container(
width: 1, width: 1,
@ -702,7 +734,6 @@ class _EditorToolbarState extends State<EditorToolbar> {
); );
} }
IconData _buildTextAlignIcon(TextAlign align) { IconData _buildTextAlignIcon(TextAlign align) {
switch (align) { switch (align) {
case TextAlign.left: case TextAlign.left:
@ -716,13 +747,9 @@ class _EditorToolbarState extends State<EditorToolbar> {
case TextAlign.justify: case TextAlign.justify:
return Icons.format_align_justify; return Icons.format_align_justify;
} }
return null;
} }
*/
} }
/*
enum _TextType { enum _TextType {
header1, header1,
header2, header2,
@ -732,7 +759,6 @@ enum _TextType {
orderedListItem, orderedListItem,
unorderedListItem, unorderedListItem,
} }
*/
/// Small toolbar that is intended to display over an image and /// Small toolbar that is intended to display over an image and
/// offer controls to expand or contract the size of the image. /// offer controls to expand or contract the size of the image.
@ -760,7 +786,7 @@ class ImageFormatToolbar extends StatefulWidget {
/// The [composer] provides access to the user's current /// The [composer] provides access to the user's current
/// selection within the document, which dictates the /// selection within the document, which dictates the
/// content that is altered by the toolbar's options. /// content that is altered by the toolbar's options.
final DocumentComposer? composer; final DocumentComposer composer;
/// Callback that should update the width of the component with /// Callback that should update the width of the component with
/// the given [nodeId] to match the given [width]. /// the given [nodeId] to match the given [width].
@ -771,16 +797,16 @@ class ImageFormatToolbar extends StatefulWidget {
final VoidCallback closeToolbar; final VoidCallback closeToolbar;
@override @override
_ImageFormatToolbarState createState() => _ImageFormatToolbarState(); State<ImageFormatToolbar> createState() => _ImageFormatToolbarState();
} }
class _ImageFormatToolbarState extends State<ImageFormatToolbar> { class _ImageFormatToolbarState extends State<ImageFormatToolbar> {
void _makeImageConfined() { void _makeImageConfined() {
widget.setWidth(widget.composer!.selection!.extent.nodeId, null); widget.setWidth(widget.composer.selection!.extent.nodeId, null);
} }
void _makeImageFullBleed() { void _makeImageFullBleed() {
widget.setWidth(widget.composer!.selection!.extent.nodeId, double.infinity); widget.setWidth(widget.composer.selection!.extent.nodeId, double.infinity);
} }
@override @override
@ -789,8 +815,9 @@ class _ImageFormatToolbarState extends State<ImageFormatToolbar> {
anchor: widget.anchor, anchor: widget.anchor,
composer: widget.composer, composer: widget.composer,
child: ValueListenableBuilder<DocumentSelection?>( child: ValueListenableBuilder<DocumentSelection?>(
valueListenable: widget.composer!.selectionNotifier, valueListenable: widget.composer.selectionNotifier,
builder: (context, selection, child) { builder: (context, selection, child) {
//appLog.fine("Building image toolbar. Selection: $selection");
if (selection == null) { if (selection == null) {
return const SizedBox(); return const SizedBox();
} }
@ -825,6 +852,7 @@ class _ImageFormatToolbarState extends State<ImageFormatToolbar> {
onPressed: _makeImageConfined, onPressed: _makeImageConfined,
icon: const Icon(Icons.photo_size_select_large), icon: const Icon(Icons.photo_size_select_large),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelBold,
tooltip: 'Bold', tooltip: 'Bold',
), ),
), ),
@ -833,6 +861,7 @@ class _ImageFormatToolbarState extends State<ImageFormatToolbar> {
onPressed: _makeImageFullBleed, onPressed: _makeImageFullBleed,
icon: const Icon(Icons.photo_size_select_actual), icon: const Icon(Icons.photo_size_select_actual),
splashRadius: 16, splashRadius: 16,
//tooltip: AppLocalizations.of(context)!.labelItalics,
tooltip: 'Italics', tooltip: 'Italics',
), ),
), ),
@ -853,7 +882,7 @@ class _PositionedToolbar extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
final ValueNotifier<Offset?> anchor; final ValueNotifier<Offset?> anchor;
final DocumentComposer? composer; final DocumentComposer composer;
final Widget child; final Widget child;
@override @override
@ -861,12 +890,16 @@ class _PositionedToolbar extends StatelessWidget {
return ValueListenableBuilder<Offset?>( return ValueListenableBuilder<Offset?>(
valueListenable: anchor, valueListenable: anchor,
builder: (context, offset, _) { builder: (context, offset, _) {
if (offset == null || composer!.selection == null) { //appLog.fine(
// "(Re)Building _PositionedToolbar widget due to anchor change");
if (offset == null || composer.selection == null) {
//appLog.fine("Anchor is null. Building an empty box.");
// When no anchor position is available, or the user hasn't // When no anchor position is available, or the user hasn't
// selected any text, show nothing. // selected any text, show nothing.
return const SizedBox(); return const SizedBox();
} }
//appLog.fine("Anchor is non-null: $offset, child: $child");
return SizedBox.expand( return SizedBox.expand(
child: Stack( child: Stack(
children: [ children: [

View File

@ -21,10 +21,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.6" version: "3.4.9"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -42,12 +42,13 @@ packages:
source: hosted source: hosted
version: "2.11.0" version: "2.11.0"
attributed_text: attributed_text:
dependency: transitive dependency: "direct overridden"
description: description:
name: attributed_text path: attributed_text
sha256: e43495051b63e6cdbe96aa62123974074cca109d9c56f74ce2ffaec8060e044e ref: stable
url: "https://pub.dev" resolved-ref: c040c690f1dc0f7c482d8ece02622c200cb8e1b1
source: hosted url: "https://github.com/superlistapp/super_editor"
source: git
version: "0.2.2" version: "0.2.2"
barcode: barcode:
dependency: transitive dependency: transitive
@ -102,10 +103,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_daemon name: build_daemon
sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
@ -118,10 +119,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.6" version: "2.4.7"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
@ -142,26 +143,42 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: built_value name: built_value
sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.6.3" version: "8.8.0"
built_value_generator: built_value_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: built_value_generator name: built_value_generator
sha256: a7a20bd4a943316c46c6e89b1a5631a2dace50ca6742b73d0b719fd243a7da00 sha256: "78680d78a6cab222fc5725ffa21065a05bba951452ae84d08b3c0e150fd3f9f6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.6.3" version: "8.8.0"
cached_network_image: cached_network_image:
dependency: "direct main" dependency: "direct main"
description: description:
name: cached_network_image name: cached_network_image
sha256: ebab9f6c55a7aa8d62cdfe0877f8b5ce806c1956b0a244ba39fcadeb0e1aad85 sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f
url: "https://pub.dev"
source: hosted
version: "3.3.0"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -222,18 +239,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.7.0" version: "4.8.0"
collection: collection:
dependency: "direct main" dependency: "direct main"
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
console: console:
dependency: transitive dependency: transitive
description: description:
@ -262,18 +279,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: coverage name: coverage
sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.3" version: "1.6.4"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c" sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+6" version: "0.3.3+8"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -310,10 +327,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: device_info_plus name: device_info_plus
sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.1.0" version: "9.1.1"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -423,14 +440,6 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_blurhash:
dependency: transitive
description:
name: flutter_blurhash
sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
description: description:
@ -469,10 +478,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.16" version: "2.0.17"
flutter_redux: flutter_redux:
dependency: "direct main" dependency: "direct main"
description: description:
@ -485,10 +494,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_slidable name: flutter_slidable
sha256: cc4231579e3eae41ae166660df717f4bad1359c87f4a4322ad8ba1befeb3d2be sha256: "19ed4813003a6ff4e9c6bcce37e792a2a358919d7603b2b31ff200229191e44c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
flutter_staggered_grid_view: flutter_staggered_grid_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -527,10 +536,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: follow_the_leader name: follow_the_leader
sha256: "40112c4fa8fdc9e60c3350f09e5b9c86e1205f4f8427feee15ef8da4c60c635c" sha256: "71f4bfca904974a98d21558bbf7489e1262da6ac46de912791fe84cf6516b9ae"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.4+5" version: "0.0.4+7"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
@ -580,18 +589,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: google_sign_in_ios name: google_sign_in_ios
sha256: "974944859f9cd40eb8a15b3fe8efb2d47fb7e99438f763f61a1ccd28d74ff4ce" sha256: "81495441405c138e3c638f5097bebaa0db644567b3976e08944cfb8926ff2e6d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.6.4" version: "5.6.5"
google_sign_in_platform_interface: google_sign_in_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: google_sign_in_platform_interface name: google_sign_in_platform_interface
sha256: "35ceee5f0eadc1c07b0b4af7553246e315c901facbb7d3dadf734ba2693ceec4" sha256: e10eaaa30a0cb03af12dd324fb2e630ac7e9d854d0530f7a87a4d825031f9a4a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.2" version: "2.4.3"
google_sign_in_web: google_sign_in_web:
dependency: transitive dependency: transitive
description: description:
@ -692,10 +701,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "0c7b83bbe2980c8a8e36e974f055e11e51675784e13a4762889feed0f3937ff2" sha256: d6a6e78821086b0b737009b09363018309bbc6de3fd88cc5c26bc2bb44a4957f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.8+1" version: "0.8.8+2"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@ -708,10 +717,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: c5538cacefacac733c724be7484377923b476216ad1ead35a0d2eadcdc0fc497 sha256: "76ec722aeea419d03aa915c2c96bf5b47214b053899088c9abb4086ceecf97a7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.8+2" version: "0.8.8+4"
image_picker_linux: image_picker_linux:
dependency: transitive dependency: transitive
description: description:
@ -764,10 +773,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: in_app_purchase_android name: in_app_purchase_android
sha256: "63997b855f10799a1022939bbf02e3f59b6f400f4deee858f46fd528df5f5fab" sha256: c4b84caa4e2c7ffebda444c5033fd8423cc3a45a6e1066929bbbcd4daf665db5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0+13" version: "0.3.0+15"
in_app_purchase_platform_interface: in_app_purchase_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -780,26 +789,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: in_app_purchase_storekit name: in_app_purchase_storekit
sha256: "88afd256c7605d431f0ce29d0161f9554851f90ecb92ceb9e18196c4e7858d52" sha256: "29526f5ce85bd908b4cacdadb2e8ef299bccbb516b90d2881805343f868502ab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.6+7" version: "0.3.7"
in_app_review: in_app_review:
dependency: "direct main" dependency: "direct main"
description: description:
name: in_app_review name: in_app_review
sha256: "16328b8202d36522322b95804ae5d975577aa9f584d634985849ba1099645850" sha256: "41ec6f30427ab09eb6ae1c85c4a2a624a145fc5d726f023de4d97170ec9e5466"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.6" version: "2.0.8"
in_app_review_platform_interface: in_app_review_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: in_app_review_platform_interface name: in_app_review_platform_interface
sha256: b12ec9aaf6b34d3a72aa95895eb252b381896246bdad4ef378d444affe8410ef sha256: fed2c755f2125caa9ae10495a3c163aa7fab5af3585a9c62ef4a6920c5b45f10
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.4" version: "2.0.5"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -844,10 +853,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: linkify name: linkify
sha256: bdfbdafec6cdc9cd0ebb333a868cafc046714ad508e48be8095208c54691d959 sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.0" version: "5.0.0"
local_auth: local_auth:
dependency: "direct main" dependency: "direct main"
description: description:
@ -860,18 +869,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: local_auth_android name: local_auth_android
sha256: "9ad0b1ffa6f04f4d91e38c2d4c5046583e23f4cae8345776a994e8670df57fb1" sha256: df4ccb3193525b8a60c78a5ca7bf188a47705bcf77bcc837a6b2cf6da64ae0e2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.34" version: "1.0.35"
local_auth_ios: local_auth_ios:
dependency: transitive dependency: transitive
description: description:
name: local_auth_ios name: local_auth_ios
sha256: "26a8d1ad0b4ef6f861d29921be8383000fda952e323a5b6752cf82ca9cf9a7a9" sha256: "8293faf72ef0ac4710f209edd03916c2d4c1eeab0483bdcf9b2e659c2f7d737b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.4" version: "1.1.5"
local_auth_platform_interface: local_auth_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -940,10 +949,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -964,10 +973,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: msix name: msix
sha256: "6e76e2491d5c809d784ce2b68e6c3426097fb5c68e61fe121c8c3341ab89bf46" sha256: "519b183d15dc9f9c594f247e2d2339d855cf0eaacc30e19b128e14f3ecc62047"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.16.4" version: "3.16.7"
navigation_builder: navigation_builder:
dependency: "direct overridden" dependency: "direct overridden"
description: description:
@ -988,10 +997,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: octo_image name: octo_image
sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "2.0.0"
overflow_view: overflow_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1028,10 +1037,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: package_info_plus name: package_info_plus
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.0" version: "5.0.1"
package_info_plus_platform_interface: package_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1068,10 +1077,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.1"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@ -1108,58 +1117,66 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: pdf name: pdf
sha256: "9f75fc7f5580ea5e635b5724de58fb27f684c9ad03ed46fdc1aac768e4557315" sha256: "93cbb2c06de9bab91844550f19896b2373e7a5ce25173995e7e5ec5e1741429d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.10.4" version: "3.10.7"
permission_handler: permission_handler:
dependency: "direct main" dependency: "direct main"
description: description:
name: permission_handler name: permission_handler
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.0.1" version: "11.1.0"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.1.0" version: "12.0.1"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.1.4" version: "9.2.0"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df"
url: "https://pub.dev"
source: hosted
version: "0.1.0+2"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.12.0" version: "4.0.2"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_windows name: permission_handler_windows
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.3" version: "0.2.0"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.0" version: "6.0.2"
pigeon: pigeon:
dependency: transitive dependency: transitive
description: description:
@ -1188,26 +1205,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.2"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
pointer_interceptor: pointer_interceptor:
dependency: "direct main" dependency: "direct main"
description: description:
name: pointer_interceptor name: pointer_interceptor
sha256: "7626e034489820fd599380d2bb4d3f4a0a5e3529370b62bfce53ab736b91adb2" sha256: adf7a637f97c077041d36801b43be08559fd4322d2127b3f20bb7be1b9eebc22
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.3+6" version: "0.9.3+7"
pointycastle: pointycastle:
dependency: transitive dependency: transitive
description: description:
@ -1228,10 +1245,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: printing name: printing
sha256: e7c383dca95ee7b88c02dc1c66638628d3dcdc2fb2cc47e7a595facd47e46b56 sha256: ad39a42a5f83125952457dfd94f395c8cf0eb1f7759583dadb769be5c7f99d24
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.11.0" version: "5.11.1"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -1325,34 +1342,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sentry name: sentry
sha256: "9cfd325611ab54b57d5e26957466823f05bea9d6cfcc8d48f11817b8bcedf0d1" sha256: e7ded42974bac5f69e4ca4ddc57d30499dd79381838f24b7e8fd9aa4139e7b79
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.12.0" version: "7.13.2"
sentry_flutter: sentry_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: sentry_flutter name: sentry_flutter
sha256: "0cd7d622cb63c94fd1b2f87ab508e158b950bd281e2a80f327ebf73bb217eaf3" sha256: d6f55ec7a1f681784165021f749007712a72ff57eadf91e963331b6ae326f089
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.12.0" version: "7.13.2"
share_plus: share_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: share_plus name: share_plus
sha256: "2dafa6c1f8d8ee67b0e0587881947b78baab4671b7c08792cf91279e7ac14192" sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.2.0" version: "7.2.1"
share_plus_platform_interface: share_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: share_plus_platform_interface name: share_plus_platform_interface
sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7" sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.0" version: "3.3.1"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1397,10 +1414,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_web name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.2"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
@ -1522,18 +1539,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0" version: "2.5.0+2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
states_rebuilder: states_rebuilder:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1546,10 +1563,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
stream_transform: stream_transform:
dependency: transitive dependency: transitive
description: description:
@ -1567,29 +1584,32 @@ packages:
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
super_editor: super_editor:
dependency: "direct main" dependency: "direct overridden"
description: description:
name: super_editor path: super_editor
sha256: "2d5acf95449f53eec1c7d0788530b3a667758224a1e4aa4164bd5d6c0d159bab" ref: stable
url: "https://pub.dev" resolved-ref: c040c690f1dc0f7c482d8ece02622c200cb8e1b1
source: hosted url: "https://github.com/superlistapp/super_editor"
source: git
version: "0.2.6" version: "0.2.6"
super_editor_markdown: super_editor_markdown:
dependency: "direct main" dependency: "direct overridden"
description: description:
name: super_editor_markdown path: super_editor_markdown
sha256: "2515d0183ee21aa22d577e95e80b1e4bc1b9ce0f651127d40844be7cb58b5330" ref: stable
url: "https://pub.dev" resolved-ref: c040c690f1dc0f7c482d8ece02622c200cb8e1b1
source: hosted url: "https://github.com/superlistapp/super_editor"
source: git
version: "0.1.5" version: "0.1.5"
super_text_layout: super_text_layout:
dependency: transitive dependency: "direct overridden"
description: description:
name: super_text_layout path: super_text_layout
sha256: "2f2a8b36553f775c390924f079b5a8ba6c717b0885f44d80a9602bfa182b6f9f" ref: stable
url: "https://pub.dev" resolved-ref: c040c690f1dc0f7c482d8ece02622c200cb8e1b1
source: hosted url: "https://github.com/superlistapp/super_editor"
version: "0.1.7" source: git
version: "0.1.8"
sync_http: sync_http:
dependency: transitive dependency: transitive
description: description:
@ -1618,34 +1638,34 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: test name: test
sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.24.3" version: "1.24.9"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.3" version: "0.5.9"
timeago: timeago:
dependency: "direct main" dependency: "direct main"
description: description:
name: timeago name: timeago
sha256: "4addcda362e51f23cf7ae2357fccd053f29d59b4ddd17fb07fc3e7febb47a456" sha256: c44b80cbc6b44627c00d76960f2af571f6f50e5dbedef4d9215d455e4335165b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.5.0" version: "3.6.0"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -1682,66 +1702,66 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.14" version: "6.2.1"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "6.2.0"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.5" version: "6.2.1"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_linux name: url_launcher_linux
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.6" version: "3.1.0"
url_launcher_macos: url_launcher_macos:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.7" version: "3.1.0"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_platform_interface name: url_launcher_platform_interface
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.5" version: "2.2.0"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.20" version: "2.2.1"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_windows name: url_launcher_windows
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.8" version: "3.1.0"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
@ -1778,10 +1798,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.7.1" version: "11.10.0"
vs_scrollbar: vs_scrollbar:
dependency: transitive dependency: transitive
description: description:
@ -1802,10 +1822,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -1834,34 +1854,34 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: c1ab9b81090705c6069197d9fdc1625e587b52b8d70cdde2339d177ad0dbb98e sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.1" version: "4.4.2"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: b0cd33dd7d3dd8e5f664e11a19e17ba12c352647269921a3b568406b001f1dff sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.12.0" version: "3.12.1"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_platform_interface name: webview_flutter_platform_interface
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f" sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.0" version: "2.8.0"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: "30b9af6bdd457b44c08748b9190d23208b5165357cc2eb57914fee1366c42974" sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.9.1" version: "3.9.4"
widget_kit_plugin: widget_kit_plugin:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1874,10 +1894,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.9" version: "5.1.1"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@ -1906,10 +1926,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.5.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@ -1919,5 +1939,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.1.0 <4.0.0" dart: ">=3.2.0 <4.0.0"
flutter: ">=3.13.0" flutter: ">=3.16.0"

View File

@ -39,7 +39,8 @@ dependencies:
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,
cached_network_image: ^3.3.0
url_launcher: ^6.0.20 url_launcher: ^6.0.20
share_plus: ^7.1.0 share_plus: ^7.1.0
intl: 0.17.0 intl: 0.17.0
@ -72,13 +73,14 @@ dependencies:
contacts_service: ^0.6.3 contacts_service: ^0.6.3
diacritic: ^0.1.3 diacritic: ^0.1.3
states_rebuilder: ^6.2.0 states_rebuilder: ^6.2.0
super_editor: ^0.2.6 #super_editor: ^0.2.6
super_editor_markdown: ^0.1.5 #super_editor_markdown: ^0.1.5
#markdown: ^5.0.0 # REMOVE THIS #markdown: ^5.0.0 # REMOVE THIS
#super_editor: #super_editor_markdown:
# git: # git:
# url: https://github.com/superlistapp/super_editor.git # url: https://github.com/superlistapp/super_editor.git
# path: super_editor # path: super_editor_markdown
# ref: stable
html2md: ^1.2.6 html2md: ^1.2.6
printing: ^5.11.0 printing: ^5.11.0
image_cropper: ^4.0.1 image_cropper: ^4.0.1
@ -101,6 +103,26 @@ dependency_overrides:
intl: any intl: any
navigation_builder: ^0.0.3 navigation_builder: ^0.0.3
states_rebuilder: ^6.3.0 states_rebuilder: ^6.3.0
super_editor:
git:
url: https://github.com/superlistapp/super_editor
path: super_editor
ref: stable
super_editor_markdown:
git:
url: https://github.com/superlistapp/super_editor
path: super_editor_markdown
ref: stable
super_text_layout:
git:
url: https://github.com/superlistapp/super_editor
path: super_text_layout
ref: stable
attributed_text:
git:
url: https://github.com/superlistapp/super_editor
path: attributed_text
ref: stable
dev_dependencies: dev_dependencies:
flutter_driver: flutter_driver:

View File

@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library === # === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG> ${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS