refraction_ui 0.47.1 copy "refraction_ui: ^0.47.1" to clipboard
refraction_ui: ^0.47.1 copied to clipboard

A headless, highly customizable, and fully accessible Flutter UI library matching Refraction UI.

example/lib/main.dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'pages/app_shell_page.dart';
import 'pages/sheet_page.dart';
import 'package:refraction_ui/refraction_ui.dart';

// Import our layouts and pages
import 'pages/home_page.dart';
import 'pages/docs_layout.dart';
import 'pages/carousel_page.dart';
import 'pages/accordion_page.dart';
import 'pages/chat_input_page.dart';
import 'pages/composer_page.dart';
import 'pages/command_menu_page.dart';
import 'pages/command_input_page.dart';
import 'pages/radio_group_page.dart';
import 'pages/progress_slider_page.dart';
import 'pages/alert_page.dart';
import 'pages/callout_page.dart';
import 'pages/avatar_page.dart';
import 'pages/date_picker_page.dart';
import 'pages/file_upload_page.dart';
import 'pages/emoji_picker_page.dart';
import 'pages/code_editor_page.dart';
import 'pages/inline_editor_page.dart';
import 'pages/rich_editor_page.dart';
import 'pages/card_grid_page.dart';
import 'pages/location_selector_page.dart';
import 'pages/language_selector_page.dart';
import 'pages/data_table_page.dart';
import 'pages/collapsible_page.dart';
import 'pages/file_tree_page.dart';
import 'pages/pagination_page.dart';
import 'pages/steps_page.dart';
import 'pages/resizable_layout_page.dart';
import 'pages/table_of_contents_page.dart';
import 'pages/video_player_page.dart';
import 'pages/waveform_page.dart';
import 'pages/animated_text_page.dart';
import 'pages/markdown_renderer_page.dart';
import 'pages/install_prompt_page.dart';
import 'pages/cookie_consent_page.dart';
import 'pages/diff_viewer_page.dart';
import 'pages/dropdown_menu_page.dart';
import 'pages/reaction_bar_page.dart';
import 'pages/device_frame_page.dart';
import 'pages/slide_viewer_page.dart';
import 'pages/charts_page.dart';
import 'pages/feedback_dialog_page.dart';
import 'pages/content_protection_page.dart';
import 'pages/form_page.dart';
import 'pages/payment_page.dart';
import 'pages/calendar_page.dart';
import 'pages/thread_view_page.dart';
import 'pages/conversation_page.dart';
import 'pages/presence_indicator_page.dart';
import 'pages/status_indicator_page.dart';
import 'pages/keyboard_shortcut_page.dart';
import 'pages/logger_page.dart';
import 'pages/version_selector_page.dart';
import 'pages/skip_to_content_page.dart';
import 'pages/mascot_page.dart';
import 'dev_tools/preview_canvas.dart';

// Import the mock applications
import 'apps/family_calendar_app.dart';
import 'apps/pregnancy_tracker_app.dart';
import 'apps/my_prototype.dart';

void main() {
  runApp(const ProviderScope(child: RefractionDemoApp()));
}

class _RouteNotifier extends Notifier<String> {
  @override
  String build() {
    // Read the initial route from the browser URL fragment on web
    String initial = '/';
    if (kIsWeb) {
      initial = Uri.base.fragment;
    } else {
      initial = WidgetsBinding.instance.platformDispatcher.defaultRouteName;
    }
    
    if (initial.isNotEmpty && initial != '/') {
      // Ensure leading slash
      return initial.startsWith('/') ? initial : '/$initial';
    }
    return '/';
  }
  void update(String val) => state = val;
}

final appRouteProvider = NotifierProvider<_RouteNotifier, String>(
  _RouteNotifier.new,
);

class _DarkModeNotifier extends Notifier<bool> {
  @override
  bool build() => true;
  void toggle() => state = !state;
}

final darkModeProvider = NotifierProvider<_DarkModeNotifier, bool>(
  _DarkModeNotifier.new,
);

class _PaletteNotifier extends Notifier<String> {
  @override
  String build() => 'Minimal';
  void update(String val) => state = val;
}

final paletteProvider = NotifierProvider<_PaletteNotifier, String>(
  _PaletteNotifier.new,
);

class RefractionDemoApp extends ConsumerWidget {
  const RefractionDemoApp({super.key});

  final List<String> components = const [
    'App Shell',
    'Accordion',
    'Collapsible',
    'Menus',
    'Command Menu',
    'Command Input',
    'Radio Group',
    'Progress Slider',
    'Alert',
    'Callout',
    'Avatar',
    'Buttons',
    'Badges',
    'Inputs & Forms',
    'Select & Dropdowns',
    'Tabs',
    'Navbar & Navigation',
    'Cards & Layouts',
    'Card Grid',
    'Charts',
    'Code Editor',
    'Inline Editor',
    'Rich Editor',
    'Date Picker',
    'Location Selector',
    'Language Selector',
    'Emoji Picker',
    'File Upload',
    'Popovers & Tooltips',
    'Toasts',
    'Data Table',
    'File Tree',
    'Pagination',
    'Resizable Layout',
    'Table of Contents',
    'Video Player',
    'Slide Viewer',
    'Waveform',
    'Animated Text',
    'Install Prompt',
    'Voice Pill',
    'Markdown Renderer',
    'Device Frame',
    'Reaction Bar',
    'Cookie Consent',
    'Feedback Dialog',
    'Content Protection',
    'Form',
    'Payment',
    'Calendar',
    'Thread View',
    'Conversation',
    'Presence Indicator',
    'Status Indicator',
    'Keyboard Shortcut',
    'Logger',
    'Version Selector',
    'Skip To Content',
    'Mascot',
  ];

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isDarkMode = ref.watch(darkModeProvider);
    final palette = ref.watch(paletteProvider);

    RefractionThemeData themeData;
    switch (palette) {
      case 'Fintech':
        themeData = isDarkMode
            ? RefractionThemeData.fintechDark()
            : RefractionThemeData.fintechLight();
        break;
      case 'Wellness':
        themeData = isDarkMode
            ? RefractionThemeData.wellnessDark()
            : RefractionThemeData.wellnessLight();
        break;
      case 'Creative':
        themeData = isDarkMode
            ? RefractionThemeData.creativeDark()
            : RefractionThemeData.creativeLight();
        break;
      case 'Productivity':
        themeData = isDarkMode
            ? RefractionThemeData.productivityDark()
            : RefractionThemeData.productivityLight();
        break;
      case 'Minimal':
      default:
        themeData = isDarkMode
            ? RefractionThemeData.minimalDark()
            : RefractionThemeData.minimalLight();
        break;
    }

    return RefractionTheme(
      data: themeData,
      child: MaterialApp(
        title: 'Refraction UI (Flutter)',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          scaffoldBackgroundColor: themeData.colors.background,
          fontFamily: themeData.fontFamily,
          useMaterial3: true,
        ),
        home: _AppShell(components: components),
      ),
    );
  }
}

class _AppShell extends ConsumerWidget {
  final List<String> components;

  const _AppShell({required this.components});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final theme = RefractionTheme.of(context).data;
    final colors = theme.colors;
    final currentRoute = ref.watch(appRouteProvider);

    Widget body;
    if (currentRoute == '/') {
      body = HomePage(
        onGetStarted: () =>
            ref.read(appRouteProvider.notifier).update('/docs/buttons'),
      );
    } else if (currentRoute.startsWith('/docs')) {
      body = DocsLayout(
        currentRoute: currentRoute,
        onNavigate: (r) => ref.read(appRouteProvider.notifier).update(r),
        components: components,
        child: _buildDocsContent(currentRoute, context),
      );
    } else {
      body = const Center(child: Text('404 Not Found'));
    }

    return Scaffold(
      backgroundColor: colors.background,
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(64),
        child: Container(
          decoration: BoxDecoration(
            color: colors.background,
            border: Border(bottom: BorderSide(color: colors.border)),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              InkWell(
                onTap: () => ref.read(appRouteProvider.notifier).update('/'),
                child: Row(
                  children: [
                    Icon(Icons.diamond, color: colors.primary, size: 28),
                    const SizedBox(width: 12),
                    Text(
                      "Refraction UI",
                      style: theme.textStyle.copyWith(
                        fontWeight: FontWeight.bold,
                        fontSize: 18,
                      ),
                    ),
                    const SizedBox(width: 8),
                    RefractionBadge(
                      variant: RefractionBadgeVariant.secondary,
                      child: const Text("Flutter v0.2.0"),
                    ),
                  ],
                ),
              ),
              Row(
                children: [
                  _buildThemePicker(context, ref, theme),
                  const SizedBox(width: 16),
                  _buildDarkModeToggle(ref, theme),
                  const SizedBox(width: 16),
                  RefractionButton(
                    variant: RefractionButtonVariant.outline,
                    onPressed: () {},
                    child: const Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Icon(Icons.code, size: 16),
                        SizedBox(width: 8),
                        Text("GitHub"),
                      ],
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
      body: body,
    );
  }

  Widget _buildDarkModeToggle(WidgetRef ref, RefractionThemeData theme) {
    final isDark = ref.watch(darkModeProvider);
    return InkWell(
      onTap: () => ref.read(darkModeProvider.notifier).toggle(),
      child: Container(
        height: 36,
        width: 36,
        decoration: BoxDecoration(
          border: Border.all(color: theme.colors.border),
          borderRadius: BorderRadius.circular(theme.borderRadius),
        ),
        child: Icon(
          isDark ? Icons.light_mode : Icons.dark_mode,
          size: 16,
          color: theme.colors.foreground,
        ),
      ),
    );
  }

  Widget _buildThemePicker(
    BuildContext context,
    WidgetRef ref,
    RefractionThemeData theme,
  ) {
    final palette = ref.watch(paletteProvider);
    final palettes = [
      {'name': 'Minimal', 'color': const Color(0xFF000000)},
      {'name': 'Fintech', 'color': const Color(0xFF00D632)},
      {'name': 'Wellness', 'color': const Color(0xFFFF6E66)},
      {'name': 'Creative', 'color': const Color(0xFF5046E5)},
      {'name': 'Productivity', 'color': const Color(0xFF3B82F6)},
    ];

    return PopupMenuButton<String>(
      onSelected: (val) => ref.read(paletteProvider.notifier).update(val),
      offset: const Offset(0, 48),
      color: theme.colors.popover,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(theme.borderRadius),
        side: BorderSide(color: theme.colors.border),
      ),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        decoration: BoxDecoration(
          border: Border.all(color: theme.colors.border),
          borderRadius: BorderRadius.circular(theme.borderRadius),
        ),
        child: Row(
          children: [
            Container(
              width: 12,
              height: 12,
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color:
                    palettes.firstWhere((p) => p['name'] == palette)['color']
                        as Color,
              ),
            ),
            const SizedBox(width: 8),
            Text(palette, style: theme.textStyle.copyWith(fontSize: 14)),
            const SizedBox(width: 4),
            Icon(
              Icons.keyboard_arrow_down,
              size: 16,
              color: theme.colors.foreground,
            ),
          ],
        ),
      ),
      itemBuilder: (context) {
        return palettes.map((p) {
          final isSelected = p['name'] == palette;
          return PopupMenuItem<String>(
            value: p['name'] as String,
            child: Row(
              children: [
                Container(
                  width: 12,
                  height: 12,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: p['color'] as Color,
                  ),
                ),
                const SizedBox(width: 12),
                Text(
                  p['name'] as String,
                  style: theme.textStyle.copyWith(
                    fontWeight: isSelected
                        ? FontWeight.bold
                        : FontWeight.normal,
                  ),
                ),
              ],
            ),
          );
        }).toList();
      },
    );
  }

  // Very simple mocked content mapper
  Widget _buildDocsContent(String route, BuildContext context) {
    switch (route) {
      case '/docs/mascot':
        return const MascotPage();
      case '/docs/install-prompt':
        return const InstallPromptPage();
      case '/docs/reaction-bar':
        return const ReactionBarPage();
      case '/docs/feedback-dialog':
        return const FeedbackDialogPage();
      case '/docs/diff-viewer':
        return const DiffViewerPage();
      case '/docs/accordion':
        return AccordionPage();
      case '/docs/collapsible':
        return const CollapsiblePage();
      case '/docs/dropdown-menu':
        return const DropdownMenuPage();
      case '/docs/command-menu':
        return const CommandMenuPage();
      case '/docs/command-input':
        return const CommandInputPage();
      case '/docs/radio-group':
        return const RadioGroupPage();
      case '/docs/progress-slider':
        return const ProgressSliderPage();
      case '/docs/sheet':
        return const SheetPage();
      case '/docs/alert':
        return const AlertPage();
      case '/docs/callout':
        return const CalloutPage();
      case '/docs/avatar':
        return const AvatarPage();
      case '/docs/file-upload':
        return const FileUploadPage();
      case '/docs/emoji-picker':
        return const EmojiPickerPage();
      case '/docs/code-editor':
        return const CodeEditorPage();
      case '/docs/inline-editor':
        return const InlineEditorPage();
      case '/docs/card-grid':
        return const CardGridPage();
      case '/docs/rich-editor':
        return const RichEditorPage();
      case '/docs/date-picker':
        return const DatePickerPage();
      case '/docs/location-selector':
        return const LocationSelectorPage();
      case '/docs/language-selector':
        return const LanguageSelectorPage();
      case '/docs/data-table':
        return const DataTablePage();
      case '/docs/file-tree':
        return const FileTreePage();
      case '/docs/steps':
        return const StepsPage();
      case '/docs/pagination':
        return const PaginationPage();
      case '/docs/cookie-consent':
        return const CookieConsentPage();
      case '/docs/content-protection':
        return const ContentProtectionPage();
      case '/docs/form':
        return const FormPage();
      case '/docs/payment':
        return const PaymentPage();
      case '/docs/calendar':
        return const CalendarPage();
      case '/docs/thread-view':
        return const ThreadViewPage();
      case '/docs/conversation':
        return const ConversationPage();
      case '/docs/presence-indicator':
        return const PresenceIndicatorPage();
      case '/docs/status-indicator':
        return const StatusIndicatorPage();
      case '/docs/keyboard-shortcut':
        return const KeyboardShortcutPage();
      case '/docs/logger':
        return const LoggerPage();
      case '/docs/version-selector':
        return const VersionSelectorPage();
      case '/docs/skip-to-content':
        return const SkipToContentPage();
      case '/docs/pregnancy-tracker':
        return const PreviewCanvas(
          title: "Pregnancy Tracker Layout",
          description:
              "A full stateful demonstration of medical health/wellness tracking using the Refraction architecture.",
          fill: true,
          child: PregnancyTrackerApp(),
        );
      case '/docs/family-calendar':
        return const PreviewCanvas(
          title: "Family Calendar Layout",
          description:
              "A rich data-dense grid structure for productivity displays.",
          fill: true,
          child: FamilyCalendarApp(),
        );
      case '/docs/my-prototype':
        return const PreviewCanvas(
          title: "My Prototype",
          description: "Your local sandbox prototyping environment.",
          fill: true,
          child: MyPrototypeApp(),
        );
      case '/docs/buttons':
        return PreviewCanvas(
          title: "Buttons",
          description: "Primary actions and CTAs.",
          child: Center(
            child: Wrap(
              spacing: 16,
              runSpacing: 16,
              children: [
                RefractionButton(
                  onPressed: () {},
                  child: const Text("Primary"),
                ),
                RefractionButton(
                  variant: RefractionButtonVariant.secondary,
                  onPressed: () {},
                  child: const Text("Secondary"),
                ),
                RefractionButton(
                  variant: RefractionButtonVariant.outline,
                  onPressed: () {},
                  child: const Text("Outline"),
                ),
                RefractionButton(
                  variant: RefractionButtonVariant.ghost,
                  onPressed: () {},
                  child: const Text("Ghost"),
                ),
                RefractionButton(
                  variant: RefractionButtonVariant.destructive,
                  onPressed: () {},
                  child: const Text("Destructive"),
                ),
              ],
            ),
          ),
        );
      case '/docs/badges':
        return PreviewCanvas(
          title: "Badges",
          description: "Small status indicators and labels.",
          child: Center(
            child: Wrap(
              spacing: 16,
              children: const [
                RefractionBadge(child: Text("Primary")),
                RefractionBadge(
                  variant: RefractionBadgeVariant.secondary,
                  child: Text("Secondary"),
                ),
                RefractionBadge(
                  variant: RefractionBadgeVariant.outline,
                  child: Text("Outline"),
                ),
                RefractionBadge(
                  variant: RefractionBadgeVariant.destructive,
                  child: Text("Destructive"),
                ),
              ],
            ),
          ),
        );
      case '/docs/inputs-&-forms':
        return PreviewCanvas(
          title: "Inputs & Forms",
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 400),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const RefractionInput(placeholder: "Email address"),
                const SizedBox(height: 16),
                const RefractionInput(
                  placeholder: "Password",
                  obscureText: true,
                ),
                const SizedBox(height: 16),
                const RefractionInput(
                  placeholder: "Disabled Input",
                  disabled: true,
                ),
                const SizedBox(height: 16),
                RefractionButton(
                  onPressed: () {},
                  child: const Center(child: Text("Submit")),
                ),
              ],
            ),
          ),
        );
      case '/docs/chat-input':
        return ChatInputPage();
      case '/docs/composer':
        return ComposerPage();
      case '/docs/select-&-dropdowns':
        return PreviewCanvas(
          title: "Select & Dropdowns",
          description: "Display a list of choices on temporary surfaces.",
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 300),
              child: RefractionSelect<String>(
                value: "Option 1",
                placeholder: "Select an option",
                items: const [
                  DropdownMenuItem(value: "Option 1", child: Text("Option 1")),
                  DropdownMenuItem(value: "Option 2", child: Text("Option 2")),
                  DropdownMenuItem(value: "Option 3", child: Text("Option 3")),
                ],
                onChanged: (val) {},
              ),
            ),
          ),
        );
      case '/docs/tabs':
        return PreviewCanvas(
          title: "Tabs",
          description: "A set of layered sections of content.",
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 400),
              child: RefractionTabs(
                tabs: const ['Account', 'Password', 'Settings'],
                children: const [
                  Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text("Make changes to your account here."),
                  ),
                  Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text("Change your password here."),
                  ),
                  Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text("Manage your settings here."),
                  ),
                ],
              ),
            ),
          ),
        );
      case '/docs/app-shell':
        return const AppShellPage();
      case '/docs/carousel':
        return const CarouselPage();
      case '/docs/cards-&-layouts':
        return PreviewCanvas(
          title: "Cards",
          description: "Displays a card with header, content, and footer.",
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 350),
              child: RefractionCard(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const RefractionCardHeader(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          RefractionCardTitle("Create project"),
                          SizedBox(height: 4),
                          RefractionCardDescription(
                            "Deploy your new project in one-click.",
                          ),
                        ],
                      ),
                    ),
                    const RefractionCardContent(
                      child: RefractionInput(
                        placeholder: "Name of your project",
                      ),
                    ),
                    RefractionCardFooter(
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          RefractionButton(
                            variant: RefractionButtonVariant.outline,
                            onPressed: () {},
                            child: const Text("Cancel"),
                          ),
                          RefractionButton(
                            onPressed: () {},
                            child: const Text("Deploy"),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      case '/docs/popovers-&-tooltips':
        return PreviewCanvas(
          title: "Popovers & Tooltips",
          description: "Floating interactive panels.",
          child: Center(
            child: Wrap(
              spacing: 32,
              children: [
                RefractionTooltip(
                  message: const Text("Add to library"),
                  child: RefractionButton(
                    variant: RefractionButtonVariant.outline,
                    onPressed: () {},
                    child: const Text("Hover me"),
                  ),
                ),
                RefractionPopover(
                  content: const Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text("Popover content goes here."),
                  ),
                  trigger: RefractionButton(
                    onPressed: () {},
                    child: const Text("Click for Popover"),
                  ),
                ),
              ],
            ),
          ),
        );
      case '/docs/toasts':
        return PreviewCanvas(
          title: "Toasts",
          description: "A succinct message that is displayed temporarily.",
          child: Center(
            child: RefractionButton(
              onPressed: () {
                RefractionToast.show(
                  context: context,
                  title: "Scheduled: Catch up",
                  description: "Friday, February 10, 2024 at 5:57 PM",
                );
              },
              child: const Text("Show Toast"),
            ),
          ),
        );
      case '/docs/navbar-&-navigation':
        return const PreviewCanvas(
          title: "Navbar",
          description: "Top-level routing components.",
          child: RefractionNavbar(
            logo: Text("Brand"),
            currentPath: "/home",
            links: [
              NavLink(label: "Home", href: "/home"),
              NavLink(label: "Features", href: "/features"),
              NavLink(label: "Pricing", href: "/pricing"),
            ],
          ),
        );
      case '/docs/introduction':
        return _buildArticlePage(
          context,
          "Introduction",
          "Refraction UI Flutter is a port of the world-class Refraction UI framework. Designed with painstaking attention to detail, it maps perfectly to Mobbin archetypes, offering you pure production-ready aesthetics instantly.\n\nUse this library to stop reinventing the wheel and build professional, beautiful Flutter web and mobile applications.",
        );
      case '/docs/installation':
        return _buildArticlePage(
          context,
          "Installation",
          "Add the following to your pubspec.yaml:\n\n```yaml\ndependencies:\n  refraction_ui: ^0.2.0\n```\n\nRun `flutter pub get` and wrap your app in a `RefractionTheme` to inject the design tokens globally.",
        );
      case '/docs/theming':
        return _buildArticlePage(
          context,
          "Theming",
          "Refraction UI features 5 beautifully curated archetypes out of the box: Minimal, Fintech, Wellness, Creative, and Productivity. \n\nYou can easily construct these factories like so:\n`RefractionThemeData.minimalDark()` or `RefractionThemeData.fintechLight()` and supply them directly to the `RefractionTheme` container.",
        );
      case '/docs/resizable-layout':
        return const ResizableLayoutPage();
      case '/docs/table-of-contents':
        return const TableOfContentsPage();
      case '/docs/video-player':
        return const VideoPlayerPage();
      case '/docs/waveform':
        return const WaveformPage();
      case '/docs/charts':
        return const ChartsPage();
      case '/docs/markdown-renderer':
        return const MarkdownRendererPage();
      case '/docs/device-frame':
        return const DeviceFramePage();
      case '/docs/slide-viewer':
        return const SlideViewerPage();
      case '/docs/animated-text':
        return const AnimatedTextPage();

      default:
        // Render a generic placeholder for other components
        final title = route.split('/').last.replaceAll('-', ' ').toUpperCase();
        return PreviewCanvas(
          title: title,
          description: "Component documentation is rendering cleanly.",
          child: const Center(child: Text("Preview rendered here.")),
        );
    }
  }

  Widget _buildArticlePage(BuildContext context, String title, String body) {
    final theme = RefractionTheme.of(context).data;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: theme.textStyle.copyWith(
            fontSize: 48,
            fontWeight: FontWeight.w800,
            letterSpacing: -1,
          ),
        ),
        const SizedBox(height: 24),
        Container(
          width: double.infinity,
          height: 1,
          color: theme.colors.border,
        ),
        const SizedBox(height: 32),
        Text(
          body,
          style: theme.textStyle.copyWith(
            fontSize: 18,
            height: 1.6,
            color: theme.colors.mutedForeground,
          ),
        ),
      ],
    );
  }
}
1
likes
0
points
355
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

A headless, highly customizable, and fully accessible Flutter UI library matching Refraction UI.

Homepage
Repository (GitHub)
View/report issues

Topics

#ui #widget #design-system #components #theme

License

unknown (license)

Dependencies

characters, flutter, flutter_markdown, flutter_svg, lottie, markdown, video_player

More

Packages that depend on refraction_ui