multiline_prefix_text_field 0.0.2 copy "multiline_prefix_text_field: ^0.0.2" to clipboard
multiline_prefix_text_field: ^0.0.2 copied to clipboard

A Flutter widget that provides a multiline text input field with automatically numbered prefixes (1., 2., 3., etc.). Perfect for creating numbered lists, todo items, or any scenario where you need a s [...]

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:multiline_prefix_text_field/multiline_prefix_text_field.dart';
import 'widgets/unfocus_on_tap_outside_field_handler.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Multiline Prefix TextField Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6366F1),
          brightness: Brightness.light,
        ),
        textTheme: GoogleFonts.plusJakartaSansTextTheme(),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF818CF8),
          brightness: Brightness.dark,
        ),
        textTheme: GoogleFonts.plusJakartaSansTextTheme(
          ThemeData.dark().textTheme,
        ),
        useMaterial3: true,
      ),
      home: UnfocusOnTapOutsideFieldHandler(
        child: const DemoPage(),
      ),
    );
  }
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  List<String> _todoItems = [
    'Buy groceries for the week',
    'Complete Flutter project documentation',
    'Schedule dentist appointment',
  ];

  List<String> _notes = [
    'Remember to call Mom',
    'Review pull request #42',
  ];

  bool _isReadOnly = false;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final isDark = Theme.of(context).brightness == Brightness.dark;

    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: isDark
                ? [
              const Color(0xFF1E1B4B),
              const Color(0xFF312E81),
              const Color(0xFF1E1B4B),
            ]
                : [
              const Color(0xFFF5F3FF),
              const Color(0xFFEDE9FE),
              const Color(0xFFF5F3FF),
            ],
          ),
        ),
        child: SafeArea(
          child: CustomScrollView(
            slivers: [
              SliverToBoxAdapter(
                child: Padding(
                  padding: const EdgeInsets.all(24.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Header
                      Row(
                        children: [
                          Container(
                            padding: const EdgeInsets.all(12),
                            decoration: BoxDecoration(
                              color: colorScheme.primaryContainer,
                              borderRadius: BorderRadius.circular(16),
                            ),
                            child: Icon(
                              Icons.format_list_numbered_rounded,
                              color: colorScheme.onPrimaryContainer,
                              size: 28,
                            ),
                          ),
                          const SizedBox(width: 16),
                          Expanded(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(
                                  'Multiline Prefix',
                                  style: GoogleFonts.spaceGrotesk(
                                    fontSize: 28,
                                    fontWeight: FontWeight.bold,
                                    color: colorScheme.onSurface,
                                  ),
                                ),
                                Text(
                                  'TextField Demo',
                                  style: GoogleFonts.spaceGrotesk(
                                    fontSize: 28,
                                    fontWeight: FontWeight.bold,
                                    color: colorScheme.primary,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      Text(
                        'A numbered list text input with automatic line management',
                        style: TextStyle(
                          fontSize: 14,
                          color: colorScheme.onSurface.withValues(alpha: 0.7),
                        ),
                      ),
                      const SizedBox(height: 32),

                      // Read-only toggle
                      _buildCard(
                        colorScheme: colorScheme,
                        child: Row(
                          children: [
                            Icon(
                              _isReadOnly ? Icons.lock_rounded : Icons.edit_rounded,
                              color: colorScheme.primary,
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: Text(
                                'Read-only Mode',
                                style: TextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.w600,
                                  color: colorScheme.onSurface,
                                ),
                              ),
                            ),
                            Switch.adaptive(
                              value: _isReadOnly,
                              onChanged: (value) {
                                setState(() => _isReadOnly = value);
                              },
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(height: 24),

                      // Todo List Section
                      _buildSectionHeader(
                        icon: Icons.check_circle_outline_rounded,
                        title: 'Todo List',
                        subtitle: 'Press Enter to add new items',
                        colorScheme: colorScheme,
                      ),
                      const SizedBox(height: 12),
                      _buildCard(
                        colorScheme: colorScheme,
                        child: MultilinePrefixTextField(
                          descriptionList: _todoItems,
                          onFinishEditing: (items) {
                            setState(() => _todoItems = items);
                          },
                          isReadOnly: _isReadOnly,
                        ),
                      ),
                      const SizedBox(height: 24),

                      // Notes Section
                      _buildSectionHeader(
                        icon: Icons.note_alt_outlined,
                        title: 'Quick Notes',
                        subtitle: 'Backspace at start to merge lines',
                        colorScheme: colorScheme,
                      ),
                      const SizedBox(height: 12),
                      _buildCard(
                        colorScheme: colorScheme,
                        child: MultilinePrefixTextField(
                          descriptionList: _notes,
                          onFinishEditing: (items) {
                            setState(() => _notes = items);
                          },
                          isReadOnly: _isReadOnly,
                        ),
                      ),
                      const SizedBox(height: 24),

                      // Output Preview Section
                      _buildSectionHeader(
                        icon: Icons.data_object_rounded,
                        title: 'Current Data',
                        subtitle: 'Real-time list output',
                        colorScheme: colorScheme,
                      ),
                      const SizedBox(height: 12),
                      _buildCard(
                        colorScheme: colorScheme,
                        backgroundColor: isDark
                            ? const Color(0xFF1E1E2E)
                            : const Color(0xFFF8FAFC),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              'Todo Items:',
                              style: GoogleFonts.firaCode(
                                fontSize: 12,
                                color: colorScheme.primary,
                                fontWeight: FontWeight.w600,
                              ),
                            ),
                            const SizedBox(height: 4),
                            Text(
                              _todoItems.toString(),
                              style: GoogleFonts.firaCode(
                                fontSize: 12,
                                color: colorScheme.onSurface.withValues(alpha: 0.8),
                              ),
                            ),
                            const SizedBox(height: 16),
                            Text(
                              'Notes:',
                              style: GoogleFonts.firaCode(
                                fontSize: 12,
                                color: colorScheme.primary,
                                fontWeight: FontWeight.w600,
                              ),
                            ),
                            const SizedBox(height: 4),
                            Text(
                              _notes.toString(),
                              style: GoogleFonts.firaCode(
                                fontSize: 12,
                                color: colorScheme.onSurface.withValues(alpha: 0.8),
                              ),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(height: 32),

                      // Instructions
                      Container(
                        padding: const EdgeInsets.all(16),
                        decoration: BoxDecoration(
                          color: colorScheme.primaryContainer.withValues(alpha: 0.3),
                          borderRadius: BorderRadius.circular(16),
                          border: Border.all(
                            color: colorScheme.primary.withValues(alpha: 0.2),
                          ),
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Row(
                              children: [
                                Icon(
                                  Icons.lightbulb_outline_rounded,
                                  color: colorScheme.primary,
                                  size: 20,
                                ),
                                const SizedBox(width: 8),
                                Text(
                                  'How to use',
                                  style: TextStyle(
                                    fontSize: 14,
                                    fontWeight: FontWeight.bold,
                                    color: colorScheme.primary,
                                  ),
                                ),
                              ],
                            ),
                            const SizedBox(height: 12),
                            _buildInstruction(
                              '↵',
                              'Press Enter to create a new line',
                              colorScheme,
                            ),
                            _buildInstruction(
                              '⌫',
                              'Backspace at line start to merge with previous',
                              colorScheme,
                            ),
                            _buildInstruction(
                              '🔢',
                              'Line numbers update automatically',
                              colorScheme,
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildSectionHeader({
    required IconData icon,
    required String title,
    required String subtitle,
    required ColorScheme colorScheme,
  }) {
    return Row(
      children: [
        Icon(icon, color: colorScheme.primary, size: 20),
        const SizedBox(width: 8),
        Text(
          title,
          style: TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
            color: colorScheme.onSurface,
          ),
        ),
        const SizedBox(width: 8),
        Text(
          '• $subtitle',
          style: TextStyle(
            fontSize: 12,
            color: colorScheme.onSurface.withValues(alpha: 0.5),
          ),
        ),
      ],
    );
  }

  Widget _buildCard({
    required ColorScheme colorScheme,
    required Widget child,
    Color? backgroundColor,
  }) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: backgroundColor ?? colorScheme.surface,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: colorScheme.shadow.withValues(alpha: 0.08),
            blurRadius: 24,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: child,
    );
  }

  Widget _buildInstruction(
      String symbol,
      String text,
      ColorScheme colorScheme,
      ) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Row(
        children: [
          Container(
            width: 28,
            height: 28,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: colorScheme.surface,
              borderRadius: BorderRadius.circular(6),
            ),
            child: Text(
              symbol,
              style: TextStyle(
                fontSize: 12,
                fontWeight: FontWeight.bold,
                color: colorScheme.onSurface,
              ),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Text(
              text,
              style: TextStyle(
                fontSize: 13,
                color: colorScheme.onSurface.withValues(alpha: 0.8),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
0
points
155
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter widget that provides a multiline text input field with automatically numbered prefixes (1., 2., 3., etc.). Perfect for creating numbered lists, todo items, or any scenario where you need a structured, line-numbered text input.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on multiline_prefix_text_field