From 40f726eac7575478eeb8506ca815df46d3d633c5 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Wed, 10 Apr 2024 14:40:51 -0300 Subject: [PATCH] Adding VWText component --- docs/vw_text.md | 62 +++++++++++++ example/lib/app.dart | 2 + example/lib/examples/vw_text_example.dart | 34 +++++++ example/lib/home.dart | 6 ++ lib/vw.dart | 1 + lib/vw_text.dart | 106 ++++++++++++++++++++++ test/vw_text_test.dart | 22 +++++ 7 files changed, 233 insertions(+) create mode 100644 docs/vw_text.md create mode 100644 example/lib/examples/vw_text_example.dart create mode 100644 lib/vw_text.dart create mode 100644 test/vw_text_test.dart diff --git a/docs/vw_text.md b/docs/vw_text.md new file mode 100644 index 0000000..dd13c07 --- /dev/null +++ b/docs/vw_text.md @@ -0,0 +1,62 @@ +# VWText + +A widget that displays text with customizable styles for specific words. + +## Usage + +```dart +import 'package:vw/vw.dart'; + +class MyWidget extends StatelessWidget { + const MyWidget({super.key}); + + @override + Widget build(BuildContext context) { + return VWText( + text: "Lorem [bold] ipsum dolor sit amet. [Mark] consectetur adipiscing elit. [Deleted]", + primaryColor: Colors.red, + markColor: const Color.fromARGB(255, 196, 228, 255), + textStyle: const TextStyle( + fontSize: 20, + height: 1.6, + ), + ); + } +} +``` + +## Properties + +| Property | Type | Description | +| ------------ | ------------ | ------------------------------------ | +| text | `String` | The text to display. | +| primaryColor | `Color?` | The primary color for styled words. | +| markColor | `Color?` | The color used to mark styled words. | +| textStyle | `TextStyle?` | The text style for the whole text. | + +## Markup + +The `VWText` widget supports markup syntax to style specific words in the text. The markup syntax is based on HTML tags and can be used to apply different styles to specific words or phrases in the text. + +## Markup Syntax + +The markup syntax is a combination of HTML tags and special characters. The tags are enclosed in angle brackets (`<` and `>`) and can be used to apply different styles to the text. Before marking a word or phrase, you need to enclose it in square brackes `[]` and add the appropriate tag. + +**Example:** + +```dart +"Lorem [bold] ipsum dolor sit amet." +``` + +In this example, the word "bold" will be styled with a bold font. + +Here are some examples of how the markup syntax can be used: + +- `bold`: This will make the text bold. +- `italic`: This will make the text italic. +- `underline`: This will underline the text. +- `mark`: This will mark the text with a different color. +- `deleted`: This will make the text deleted. +- `overline`: This will make the text overline. +- `primary color`: This will use the primary color for the text. +- `uppercase`: This will make the text uppercase. diff --git a/example/lib/app.dart b/example/lib/app.dart index 91396cc..c22eb37 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -8,6 +8,7 @@ import './examples/vw_select_example.dart'; import './examples/vw_modal_example.dart'; import './examples/vw_box_example.dart'; import './examples/vw_reveal_example.dart'; +import './examples/vw_text_example.dart'; import 'home.dart'; @@ -48,6 +49,7 @@ class App extends StatelessWidget { '/vw_modal': (context) => const VWModalExample(), '/vw_box': (context) => const VWBoxExample(), '/vw_reveal': (context) => const VWRevealExample(), + '/vw_text': (context) => const VWTextExample(), }, ); } diff --git a/example/lib/examples/vw_text_example.dart b/example/lib/examples/vw_text_example.dart new file mode 100644 index 0000000..c91cd6b --- /dev/null +++ b/example/lib/examples/vw_text_example.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:vw/vw.dart'; + +class VWTextExample extends StatelessWidget { + const VWTextExample({super.key}); + + @override + Widget build(BuildContext context) { + String interpolation = "Text to be interpolated"; + + return Scaffold( + appBar: AppBar( + title: const Text('Title'), + ), + body: SingleChildScrollView( + child: VWColumn( + padding: const EdgeInsets.all(20), + children: [ + VWText( + text: + "lorem [bold adding more text here!] [text and here, we will add more text] dolor sit amet, [
    $interpolation] consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. [Ut enim ad minim veniam], quis nostrud exercitation ullamco [laboris nisi ut aliquip] ex [ea commodo consequat]. Duis aute irure dolor in reprehenderit in [voluptate] velit esse cillum dolore eu [fugiat nulla pariatur]. Excepteur sint occaecat cupidatat non proident, [sunt in culpa qui officia deserunt] mollit anim id est laborum. [Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.], quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. [Duis aute irure dolor in reprehenderit in voluptate velit esse cillum] dolore eu fugiat nulla pariatur.", + primaryColor: Colors.red, + markColor: const Color.fromARGB(255, 196, 228, 255), + textStyle: const TextStyle( + fontSize: 20, + height: 1.5, + ), + ) + ], + ), + ), + ); + } +} diff --git a/example/lib/home.dart b/example/lib/home.dart index a1b8e10..e806323 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -67,6 +67,12 @@ class Home extends StatelessWidget { Navigator.pushNamed(context, '/vw_reveal'); }, ), + ListTile( + title: const Text('VW Text'), + onTap: () { + Navigator.pushNamed(context, '/vw_text'); + }, + ) ], ), ); diff --git a/lib/vw.dart b/lib/vw.dart index eb3ee6f..e49aeec 100644 --- a/lib/vw.dart +++ b/lib/vw.dart @@ -7,3 +7,4 @@ export './vw_select.dart'; export './vw_modal.dart'; export './vw_box.dart'; export './vw_reveal.dart'; +export './vw_text.dart'; diff --git a/lib/vw_text.dart b/lib/vw_text.dart new file mode 100644 index 0000000..49eb3fb --- /dev/null +++ b/lib/vw_text.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; + +sealed class VWTextStyle { + static bool checkBold(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkItalic(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkUnderline(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkPrimaryColor(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkMark(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkDelete(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkOverline(String input) => (input.contains("") && input.contains("")) ? true : false; + static bool checkUppercase(String input) => (input.contains("") && input.contains("")) ? true : false; + + static getTextDecoration(String input) { + if (checkUnderline(input)) return TextDecoration.underline; + if (checkDelete(input)) return TextDecoration.lineThrough; + if (checkOverline(input)) return TextDecoration.overline; + + return null; + } + + static String sanitize(String input) => input + .replaceAll("", "") // + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", ""); +} + +/// A widget that displays text with customizable styles for specific words. +/// +/// The [VWText] widget allows you to display text with different styles for specific words. +/// You can customize the primary color, mark color, and text style. +class VWText extends StatefulWidget { + /// The text to display. + final String text; + + /// The primary color for styled words. + final Color? primaryColor; + + /// The color used to mark styled words. + final Color? markColor; + + /// The text style for the whole text. + final TextStyle? textStyle; + + const VWText({ + super.key, + required this.text, + this.primaryColor = Colors.red, + this.markColor = const Color.fromARGB(255, 255, 192, 192), + this.textStyle, + }); + + @override + State createState() => _VWTextState(); +} + +class _VWTextState extends State { + TextStyle defaultStyle = const TextStyle(); + + @override + Widget build(BuildContext context) { + final words = widget.text.split(RegExp(r'\[|\]')); + + List spans = []; + + for (var word in words) { + bool isUppercase = VWTextStyle.checkUppercase(word); + + var style = TextStyle( + color: VWTextStyle.checkPrimaryColor(word) ? widget.primaryColor : Theme.of(context).colorScheme.onBackground, + fontWeight: VWTextStyle.checkBold(word) ? FontWeight.w900 : null, + fontStyle: VWTextStyle.checkItalic(word) ? FontStyle.italic : null, + decoration: VWTextStyle.getTextDecoration(word), + backgroundColor: VWTextStyle.checkMark(word) ? widget.markColor : null, + ); + + word = VWTextStyle.sanitize(word); + word = isUppercase ? word.toUpperCase() : word; + + spans.add(TextSpan( + text: word, + style: style, + )); + } + + return RichText( + key: const Key('vw_text'), + text: TextSpan( + style: defaultStyle.merge(widget.textStyle), + children: spans, + ), + ); + } +} diff --git a/test/vw_text_test.dart b/test/vw_text_test.dart new file mode 100644 index 0000000..c52511d --- /dev/null +++ b/test/vw_text_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vw/vw.dart'; + +void main() { + testWidgets('Check initial render', (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: Center( + child: VWText( + text: "[bold] [italic] [underline] [mark] [deleted]", + ), + ), + ), + ), + ); + + expect(find.byKey(const Key('vw_text')), findsOneWidget); + expect(find.byKey(const Key('vw_text')), findsNWidgets(1)); + }); +}