Export the Rule class so that it's possible to add custom rules. Make the custom button apply the styles defined in the icon theme and add isToggled function to determine whether the button should apply the toggled style. Also, allow the button to be further customizable by defining a custom builder implementation. Fix the demo pages and add a demo page showcasing a custom attribute.pull/971/head
parent
1f42da82fb
commit
4face9dc23
9 changed files with 379 additions and 31 deletions
@ -0,0 +1,128 @@ |
||||
import 'dart:math'; |
||||
|
||||
import 'package:flutter/foundation.dart'; |
||||
import 'package:flutter/material.dart'; |
||||
import 'package:flutter_quill/flutter_quill.dart' hide Text; |
||||
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; |
||||
|
||||
import '../universal_ui/universal_ui.dart'; |
||||
import '../widgets/demo_scaffold.dart'; |
||||
|
||||
class CustomAttrPage extends StatefulWidget { |
||||
CustomAttrPage() { |
||||
// should probably be called when the app first starts but since the |
||||
// registry is a hashmap then it won't really matter for this example |
||||
Attribute.addCustomAttribute(const RandomColorAttribute(true)); |
||||
} |
||||
|
||||
@override |
||||
_CustomAttrPageState createState() => _CustomAttrPageState(); |
||||
} |
||||
|
||||
class _CustomAttrPageState extends State<CustomAttrPage> { |
||||
final FocusNode _focusNode = FocusNode(); |
||||
QuillController? _controller; |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return DemoScaffold( |
||||
documentFilename: 'sample_data_nomedia.json', |
||||
builder: _buildContent, |
||||
customButtons: [ |
||||
QuillCustomButton( |
||||
icon: Icons.smart_toy_sharp, |
||||
onTap: () { |
||||
if (_controller != null) { |
||||
if (_controller! |
||||
.getSelectionStyle() |
||||
.attributes |
||||
.keys |
||||
.contains(RandomColorAttribute.KEY)) { |
||||
_controller! |
||||
.formatSelection(const RandomColorAttribute(null)); |
||||
} else { |
||||
_controller! |
||||
.formatSelection(const RandomColorAttribute(true)); |
||||
} |
||||
} |
||||
}, |
||||
isToggled: () { |
||||
return _controller != null && |
||||
_controller! |
||||
.getSelectionStyle() |
||||
.attributes |
||||
.containsKey(RandomColorAttribute.KEY); |
||||
}) |
||||
], |
||||
floatingActionButton: FloatingActionButton( |
||||
onPressed: () { |
||||
setState(() { |
||||
// update ui and randomize the colors |
||||
}); |
||||
}, |
||||
child: const Icon(Icons.refresh), |
||||
), |
||||
title: 'Custom attribute demo', |
||||
); |
||||
} |
||||
|
||||
Widget _buildContent(BuildContext context, QuillController? controller) { |
||||
_controller = controller; |
||||
var quillEditor = QuillEditor( |
||||
controller: controller!, |
||||
scrollController: ScrollController(), |
||||
scrollable: true, |
||||
focusNode: _focusNode, |
||||
autoFocus: true, |
||||
readOnly: false, |
||||
expands: false, |
||||
padding: EdgeInsets.zero, |
||||
embedBuilders: FlutterQuillEmbeds.builders(), |
||||
customStyleBuilder: _customStyleBuilder, |
||||
); |
||||
if (kIsWeb) { |
||||
quillEditor = QuillEditor( |
||||
controller: controller, |
||||
scrollController: ScrollController(), |
||||
scrollable: true, |
||||
focusNode: _focusNode, |
||||
autoFocus: true, |
||||
readOnly: false, |
||||
expands: false, |
||||
padding: EdgeInsets.zero, |
||||
embedBuilders: defaultEmbedBuildersWeb, |
||||
customStyleBuilder: _customStyleBuilder, |
||||
); |
||||
} |
||||
|
||||
return Padding( |
||||
padding: const EdgeInsets.all(8), |
||||
child: Container( |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
border: Border.all(color: Colors.grey.shade200), |
||||
), |
||||
child: quillEditor, |
||||
), |
||||
); |
||||
} |
||||
|
||||
TextStyle _customStyleBuilder(Attribute attr) { |
||||
if (attr.key == RandomColorAttribute.KEY) { |
||||
// generate a random text color |
||||
return TextStyle( |
||||
color: |
||||
Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1)); |
||||
} |
||||
|
||||
return const TextStyle(); |
||||
} |
||||
} |
||||
|
||||
// custom inline attribute |
||||
class RandomColorAttribute extends Attribute<bool?> { |
||||
const RandomColorAttribute(bool? val) |
||||
: super(KEY, AttributeScope.INLINE, val); |
||||
|
||||
static const String KEY = 'random-color'; |
||||
} |
@ -1,11 +1,136 @@ |
||||
import 'package:flutter/material.dart'; |
||||
|
||||
import '../../../flutter_quill.dart'; |
||||
|
||||
class QuillCustomButton { |
||||
const QuillCustomButton({this.icon, this.onTap}); |
||||
const QuillCustomButton( |
||||
{this.icon, |
||||
this.onTap, |
||||
this.isToggled, |
||||
this.builder = defaultCustomButtonBuilder}); |
||||
|
||||
///The icon widget |
||||
// The icon widget |
||||
final IconData? icon; |
||||
|
||||
///The function when the icon is tapped |
||||
// The function when the icon is tapped |
||||
final VoidCallback? onTap; |
||||
|
||||
// The function to determine whether the button is toggled |
||||
final CustomButtonToggled? isToggled; |
||||
|
||||
// Can specify a custom builder to build the widget |
||||
final CustomButtonBuilder builder; |
||||
} |
||||
|
||||
class QuillCustomButtonWidget extends StatefulWidget { |
||||
const QuillCustomButtonWidget( |
||||
{required this.button, |
||||
required this.controller, |
||||
required this.iconSize, |
||||
this.iconTheme, |
||||
this.afterPressed}); |
||||
|
||||
final QuillCustomButton button; |
||||
final QuillController controller; |
||||
final double iconSize; |
||||
final QuillIconTheme? iconTheme; |
||||
final VoidCallback? afterPressed; |
||||
|
||||
@override |
||||
State<QuillCustomButtonWidget> createState() => |
||||
_QuillCustomButtonWidgetState(); |
||||
} |
||||
|
||||
class _QuillCustomButtonWidgetState extends State<QuillCustomButtonWidget> { |
||||
bool _toggled = false; |
||||
|
||||
@override |
||||
void initState() { |
||||
super.initState(); |
||||
|
||||
// set the initial toggled state |
||||
_toggled = |
||||
widget.button.isToggled != null ? widget.button.isToggled!() : false; |
||||
|
||||
// add listener to update the toggled state |
||||
widget.controller.addListener(_didChangeEditingValue); |
||||
} |
||||
|
||||
@override |
||||
void dispose() { |
||||
widget.controller.removeListener(_didChangeEditingValue); |
||||
super.dispose(); |
||||
} |
||||
|
||||
void _didChangeEditingValue() { |
||||
final toggled = |
||||
widget.button.isToggled != null ? widget.button.isToggled!() : false; |
||||
|
||||
if (toggled != _toggled) { |
||||
setState(() { |
||||
_toggled = toggled; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return widget.button.builder( |
||||
context, |
||||
widget.controller, |
||||
widget.button.icon, |
||||
widget.iconSize, |
||||
widget.iconTheme, |
||||
_toggled, |
||||
widget.button.onTap, |
||||
widget.afterPressed); |
||||
} |
||||
} |
||||
|
||||
typedef CustomButtonBuilder = Widget Function( |
||||
BuildContext context, |
||||
QuillController controller, |
||||
IconData? icon, |
||||
double iconSize, |
||||
QuillIconTheme? iconTheme, |
||||
bool isToggled, |
||||
VoidCallback? onPressed, |
||||
VoidCallback? afterPressed, |
||||
); |
||||
|
||||
Widget defaultCustomButtonBuilder( |
||||
BuildContext context, |
||||
QuillController controller, |
||||
IconData? icon, |
||||
double iconSize, |
||||
QuillIconTheme? iconTheme, |
||||
bool isToggled, |
||||
VoidCallback? onPressed, |
||||
VoidCallback? afterPressed, |
||||
) { |
||||
final theme = Theme.of(context); |
||||
final isEnabled = onPressed != null; |
||||
final iconColor = isEnabled |
||||
? isToggled |
||||
? (iconTheme?.iconSelectedColor ?? theme.primaryIconTheme.color) |
||||
: (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color) |
||||
: (iconTheme?.disabledIconColor ?? theme.disabledColor); |
||||
final fill = isEnabled |
||||
? isToggled |
||||
? (iconTheme?.iconSelectedFillColor ?? theme.toggleableActiveColor) |
||||
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor) |
||||
: (iconTheme?.disabledIconFillColor ?? theme.canvasColor); |
||||
|
||||
return QuillIconButton( |
||||
highlightElevation: 0, |
||||
hoverElevation: 0, |
||||
size: iconSize * kIconButtonFactor, |
||||
icon: Icon(icon, size: iconSize, color: iconColor), |
||||
fillColor: fill, |
||||
onPressed: onPressed, |
||||
afterPressed: afterPressed, |
||||
borderRadius: iconTheme?.borderRadius ?? 2, |
||||
); |
||||
} |
||||
|
||||
typedef CustomButtonToggled = bool Function(); |
||||
|
Loading…
Reference in new issue