formatus 2.0.2 copy "formatus: ^2.0.2" to clipboard
formatus: ^2.0.2 copied to clipboard

Formatus - The plain Flutter Rich-Text-Editor for all platforms

example/main.dart

import 'package:flutter/material.dart';
import 'package:formatus/formatus.dart';

/// Entry point of example application
void main() {
  runApp(const MyApp());
}

const String initialTemplateKey = 'Lists';
const Map<String, String> textTemplates = {
  'Empty': '',
  'Short':
      '<p><div style="color: #0xFF0000ff">Blue</> with <b>bold</> words</>',
  'Long': '''
<h1>Formatus Features</h1>
<h2>Text with <b>bold</b>, <i>italic</i> and <u>underlined</u> words</h2>.
<p>Third line <i>contains <s>nested</s> and</i> <u>under<b>line</b>d</u> text.</p>
''',
  'Lists': '''
  <h2>ul</h2>
  <ul><li>Apple</li><li>Kiwi</li></ul>
  <h3>ol</h3>
  <ol><li>First item</li><li>Second entry</li></ol>
  ''',
  'Anchor': '''
  <h1>Anchors</h1>
  <p>Anchor to <a href="https://icanhazdadjoke.com">dad jokes</a> is funny.</p>
  <p>Another one to <a href="https://www.duckduckgo.com">Duck Duck Go</a> search</p>
  ''',
  'Inlines': '<p><b><i><u>all</u> italic</i> bold</b> plain</p>',
};

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) => MaterialApp(
    home: const MyHomePage(),
    darkTheme: ThemeData(
      brightness: Brightness.dark,
      colorSchemeSeed: Colors.amber,
      fontFamily: 'Roboto',
      useMaterial3: true,
    ),
    theme: ThemeData(
      brightness: Brightness.light,
      colorSchemeSeed: Colors.amber,
      fontFamily: 'Roboto',
      useMaterial3: true,
    ),
    themeMode: ThemeMode.system,
  );
}

///
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<StatefulWidget> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late FormatusController controller;
  final FocusNode _formatusFocus = FocusNode(debugLabel: 'formatus');
  bool condenseActions = false;
  String savedText = '';

  @override
  void initState() {
    super.initState();
    controller = FormatusController(
      formattedText: textTemplates[initialTemplateKey] ?? '',
      onChanged: (v) => setState(() => savedText = v),
    );
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: _buildAppBar(),
    body: SafeArea(minimum: const EdgeInsets.all(16), child: _buildBody()),
  );

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  PreferredSizeWidget _buildAppBar() =>
      AppBar(title: const Text('Formatus Rich-Text-Editor'));

  Widget _buildBody() => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    mainAxisAlignment: MainAxisAlignment.start,
    children: [
      Align(alignment: Alignment.centerRight, child: _buildTextPreselection()),
      const Divider(color: Colors.deepPurpleAccent),
      FormatusBar(
        controller: controller,
        onEditAnchor: (context, anchor) => _onEditAnchor(context, anchor),
        textFieldFocus: _formatusFocus,
      ),
      TextFormField(
        buildCounter:
            (
              BuildContext context, {
              required int currentLength,
              required int? maxLength,
              required bool isFocused,
            }) => _buildCounter(currentLength, isFocused, controller.selection),
        controller: controller,
        decoration: const InputDecoration(focusedBorder: OutlineInputBorder()),
        focusNode: _formatusFocus,
        minLines: 3,
        maxLines: 7,
        showCursor: true,
      ),
      const Divider(color: Colors.deepPurpleAccent),
      SizedBox(height: 16),
      _buildFormattedText(),
      SizedBox(height: 16),
      _buildFormatusViewer(),
    ],
  );

  Widget _buildCounter(
    int currentLength,
    bool isFocused,
    TextSelection selection,
  ) => Text(
    '${selection.start}..${selection.end} of $currentLength'
    ' ${isFocused ? "focused" : ""}',
  );

  Widget _buildFormatusViewer() => Frame(
    label: 'FormatusViewer',
    child: SingleChildScrollView(
      child: SizedBox(
        height: 150,
        child: FormatusViewer(formattedText: controller.formattedText),
      ),
    ),
  );

  Widget _buildFormattedText() => Frame(
    label: 'Formatted Text',
    child: SelectableText(controller.formattedText, showCursor: true),
  );

  Widget _buildTextPreselection() => DropdownMenu<String>(
    dropdownMenuEntries: [
      for (String key in textTemplates.keys)
        DropdownMenuEntry<String>(label: key, value: key),
    ],
    initialSelection: initialTemplateKey,
    label: const Text('preselect text'),
    onSelected: (key) {
      _formatusFocus.requestFocus();
      setState(() => controller.formattedText = textTemplates[key]!);
    },
  );

  Future<FormatusAnchor?> _onEditAnchor(
    BuildContext context,
    FormatusAnchor anchor,
  ) => showAdaptiveDialog<FormatusAnchor>(
    context: context,
    builder: (BuildContext context) => AlertDialog(
      title: Row(
        children: [
          IconButton(
            icon: Icon(Icons.arrow_back),
            onPressed: () => Navigator.pop(context, anchor),
          ),
          Text(anchor.name.isEmpty ? 'Create Anchor' : 'Edit Anchor'),
          IconButton(
            icon: Icon(Icons.delete_outline),
            onPressed: () => Navigator.pop(context, anchor.clear()),
          ),
        ],
      ),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextField(
            decoration: InputDecoration(label: Text('Name')),
            onChanged: (v) => anchor.name = v,
          ),
          TextField(
            decoration: InputDecoration(label: Text('URL')),
            onChanged: (v) => anchor.href = v,
          ),
        ],
      ),
    ),
  );
}

///
/// Frame with label
///
class Frame extends StatelessWidget {
  final Widget child;
  final String label;

  const Frame({super.key, required this.label, required this.child});

  @override
  Widget build(BuildContext context) => InputDecorator(
    decoration: InputDecoration(
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
      labelText: label,
    ),
    child: child,
  );
}
1
likes
160
points
215
downloads

Publisher

unverified uploader

Weekly Downloads

Formatus - The plain Flutter Rich-Text-Editor for all platforms

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on formatus