123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- // Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file
- // for details. All rights reserved. Use of this source code is governed by a
- // BSD-style license that can be found in the LICENSE file.
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/widgets.dart';
- import 'package:zefyr/src/widgets/link.dart';
-
- import 'controller.dart';
- import 'editable_text.dart';
- import 'image.dart';
- import 'mode.dart';
- import 'scaffold.dart';
- import 'scope.dart';
- import 'theme.dart';
- import 'toolbar.dart';
-
- /// Widget for editing Zefyr documents.
- class ZefyrEditor extends StatefulWidget {
- const ZefyrEditor({
- Key key,
- @required this.controller,
- @required this.focusNode,
- this.autofocus = true,
- this.mode = ZefyrMode.edit,
- this.padding = const EdgeInsets.symmetric(horizontal: 16.0),
- this.toolbarDelegate,
- this.imageDelegate,
- this.linkDelegate,
- this.onSave,
- this.selectionControls,
- this.physics,
- this.keyboardAppearance,
- }) : assert(mode != null),
- assert(controller != null),
- assert(focusNode != null),
- super(key: key);
-
- /// Controls the document being edited.
- final ZefyrController controller;
-
- /// Controls whether this editor has keyboard focus.
- final FocusNode focusNode;
-
- /// Whether this editor should focus itself if nothing else is already
- /// focused.
- ///
- /// If true, the keyboard will open as soon as this text field obtains focus.
- /// Otherwise, the keyboard is only shown after the user taps the text field.
- ///
- /// Defaults to true. Cannot be null.
- final bool autofocus;
-
- /// Editing mode of this editor.
- final ZefyrMode mode;
-
- /// Optional delegate for customizing this editor's toolbar.
- final ZefyrToolbarDelegate toolbarDelegate;
-
- /// Delegate for resolving embedded images.
- ///
- /// This delegate is required if embedding images is allowed.
- final ZefyrImageDelegate imageDelegate;
-
- final ZefyrLinkDelegate linkDelegate;
-
- final void Function() onSave;
-
- /// Optional delegate for building the text selection handles and toolbar.
- ///
- /// If not provided then platform-specific implementation is used by default.
- final TextSelectionControls selectionControls;
-
- /// Controls physics of scrollable editor.
- final ScrollPhysics physics;
-
- /// Padding around editable area.
- final EdgeInsets padding;
-
- /// The appearance of the keyboard.
- ///
- /// This setting is only honored on iOS devices.
- ///
- /// If unset, defaults to the brightness of [ThemeData.primaryColorBrightness].
- final Brightness keyboardAppearance;
-
- @override
- _ZefyrEditorState createState() => _ZefyrEditorState();
- }
-
- class _ZefyrEditorState extends State<ZefyrEditor> {
- ZefyrImageDelegate _imageDelegate;
- ZefyrLinkDelegate _linkDelegate;
- ZefyrScope _scope;
- ZefyrThemeData _themeData;
- GlobalKey<ZefyrToolbarState> _toolbarKey;
- ZefyrScaffoldState _scaffold;
-
- bool get hasToolbar => _toolbarKey != null;
-
- void showToolbar() {
- assert(_toolbarKey == null);
- _toolbarKey = GlobalKey();
- _scaffold.showToolbar(buildToolbar);
- }
-
- void hideToolbar() {
- if (_toolbarKey == null) return;
- _scaffold.hideToolbar(buildToolbar);
- _toolbarKey = null;
- }
-
- Widget buildToolbar(BuildContext context) {
- return ZefyrTheme(
- data: _themeData,
- child: ZefyrToolbar(
- key: _toolbarKey,
- editor: _scope,
- delegate: widget.toolbarDelegate,
- ),
- );
- }
-
- void _handleChange() {
- if (_scope.focusOwner == FocusOwner.none) {
- if (!_scope.keepOverlay) {
- _scope.toolbarAction = null;
- hideToolbar();
- }
- } else if (!hasToolbar) {
- showToolbar();
- } else {
- // TODO: is there a nicer way to do this?
- WidgetsBinding.instance.addPostFrameCallback((_) {
- _toolbarKey?.currentState?.markNeedsRebuild();
- });
- }
- }
-
- @override
- void initState() {
- super.initState();
- _imageDelegate = widget.imageDelegate;
- _linkDelegate = widget.linkDelegate;
- }
-
- @override
- void didUpdateWidget(ZefyrEditor oldWidget) {
- super.didUpdateWidget(oldWidget);
- _scope.mode = widget.mode;
- _scope.controller = widget.controller;
- _scope.focusNode = widget.focusNode;
- _scope.onSave = widget.onSave;
- if (widget.imageDelegate != oldWidget.imageDelegate) {
- _imageDelegate = widget.imageDelegate;
- _scope.imageDelegate = _imageDelegate;
- }
- if (widget.linkDelegate != oldWidget.linkDelegate) {
- _linkDelegate = widget.linkDelegate;
- _scope.linkDelegate = _linkDelegate;
- }
- }
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- final parentTheme = ZefyrTheme.of(context, nullOk: true);
- final fallbackTheme = ZefyrThemeData.fallback(context);
- _themeData = (parentTheme != null)
- ? fallbackTheme.merge(parentTheme)
- : fallbackTheme;
-
- if (_scope == null) {
- _scope = ZefyrScope.editable(
- mode: widget.mode,
- imageDelegate: _imageDelegate,
- linkDelegate: _linkDelegate,
- onSave: widget.onSave,
- controller: widget.controller,
- focusNode: widget.focusNode,
- focusScope: FocusScope.of(context),
- );
- _scope.addListener(_handleChange);
- } else {
- final focusScope = FocusScope.of(context);
- _scope.focusScope = focusScope;
- }
-
- final scaffold = ZefyrScaffold.of(context);
- if (_scaffold != scaffold) {
- bool didHaveToolbar = hasToolbar;
- hideToolbar();
- _scaffold = scaffold;
- if (didHaveToolbar) showToolbar();
- }
- }
-
- @override
- void dispose() {
- hideToolbar();
- _scope.removeListener(_handleChange);
- _scope.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- final ThemeData themeData = Theme.of(context);
- final Brightness keyboardAppearance =
- widget.keyboardAppearance ?? themeData.primaryColorBrightness;
-
- Widget editable = ZefyrEditableText(
- controller: _scope.controller,
- focusNode: _scope.focusNode,
- imageDelegate: _scope.imageDelegate,
- selectionControls: widget.selectionControls,
- autofocus: widget.autofocus,
- mode: widget.mode,
- input: _scope.input,
- padding: widget.padding,
- physics: widget.physics,
- keyboardAppearance: keyboardAppearance,
- );
-
- return ZefyrTheme(
- data: _themeData,
- child: ZefyrScopeAccess(
- scope: _scope,
- child: editable,
- ),
- );
- }
- }
|