Nes UI Flutter Library

repository·main·Indexed 42 days ago

https://github.com/erickzanardo/nes_ui

A Flutter UI library inspired by retro video game consoles. It provides a themed aesthetic through customized Material widgets (Text, TextField, Divider) and entirely custom components such as NesButton, NesCheckbox, NesContainer, and NesConfirmDialog. The library includes a comprehensive theming system via flutterNesTheme(), support for the 'Press Start 2P' font, and an input management system with NesInputController and NesInputAdapter for handling NES-style input events.

Tokens
10.2K
Snippets
35
Records
45
Agent score
54%

What's inside Nes UI

  1. Overview of Nes UI widgets

    main

    Nes UI provides two types of widgets to achieve its retro video game console aesthetic:

    Material Customized Widgets

    These are standard Material widgets that have been visually customized to fit the Nes UI look. Examples include:

    • Text
    • TextField / TextFormField
    • Divider

    Custom Widgets

    These are widgets built from scratch because the Material structure could not be easily adapted to the desired retro style. Examples include:

    • NesButton
    • NesCheckbox
    • NesContainer
    • NesConfirmDialog
    • NesIcon
    • NesSelectionList
    • NesIterableOptions
  2. Install nes_ui via pubspec.yaml

    main

    To use Nes UI in your Flutter project, add nes_ui to your pubspec.yaml dependencies and run the package get command. Ensure you have the Flutter SDK installed on your machine.

    dependencies:
      nes_ui:
    flutter packages get
  3. Set up Press Start 2P font with Google Fonts

    main

    Nes UI uses the 'Press start 2P' font from the google_fonts package.

    Runtime Fetching

    If you allow the font to be fetched over HTTP at runtime, you must call WidgetsFlutterBinding.ensureInitialized(); in your main() method to ensure the binding is ready for network requests.

    Font Bundling (Assets)

    If you prefer to bundle the fonts in your app assets to avoid HTTP requests, follow the Google Fonts bundling guide. If you bundle fonts, you must also include the font license in your LicenseRegistry.

    void main() {
      LicenseRegistry.addLicense(() async* {
        final license = await rootBundle.loadString('google_fonts/OFL.txt');
        yield LicenseEntryWithLineBreaks(['google_fonts'], license);
      });
    
      runApp(...);
    }
  4. Configure the Nes UI theme

    main

    Nes UI provides a flutterNesTheme() method that returns a ThemeData object. You should pass this object to the theme property of your MaterialApp. The method accepts various arguments to allow for customization of the retro aesthetic.

    import 'package:nes_ui/nes_ui.dart';
    
    MaterialApp(
      theme: flutterNesTheme(),
    );
  5. Test nes_ui refactorings locally

    main

    The test_fixes directory contains Dart files and corresponding .expect files used to verify the package's refactorings (data-driven fixes) compatible with the dart fix command. To verify these fixes locally, navigate to the test_fixes directory and run the dart fix command with the --compare-to-golden flag.

    cd test_fixes
    dart fix --compare-to-golden
  6. How NesShakeEffect works: Declarative vs Imperative modes

    main

    The NesShakeEffect widget supports two distinct mental models for triggering animations:

    Continuous Shaking (Declarative)

    By setting shaking: true, you tell the widget to enter a loop where it shakes continuously. It will only stop when the shaking property is updated to false. This is ideal for states like "loading" or "error mode" where the UI should remain agitated.

    Single Shake (Imperative)

    By using a NesShakeEffectController, you can trigger a discrete, one-off animation. When you call controller.shake(), the widget performs a single shake sequence that decays over the specified duration and then resets. This is ideal for transient feedback, such as a haptic-like response to a user action.

  7. Configure NesDialogFrame styles

    main

    The NesDialogFrame is a sealed class used to define the visual structure of a NesDialog. You can choose between different implementations to change the look and feel of your dialogs.

    Available Frames

    1. NesBasicDialogFrame

    This is the default style. It provides a simple container with padding and an optional close button positioned at the top-right corner.

    2. NesWindowDialogFrame

    Provides a window-like appearance using NesWindow. This style is useful for more structured, desktop-like interfaces.

    • leftIcon: An optional NesIconData to display on the left side of the window.
    • title: An optional String for the window title.
    // Using the default basic style
    await NesDialog.show(
      context: context,
      builder: (context) => MyWidget(),
      frame: const NesBasicDialogFrame(),
    );
    
    // Using the window style
    await NesDialog.show(
      context: context,
      builder: (context) => MyWidget(),
      frame: const NesWindowDialogFrame(
        title: 'Settings',
        leftIcon: NesIcons.settings,
      ),
    );
  8. Initialize the Nes UI theme with flutterNesTheme()

    main

    To use Nes UI components with their intended styling, you must initialize your MaterialApp using the flutterNesTheme() function. This function returns a ThemeData object pre-configured with the necessary ThemeExtensions for all Nes UI components (buttons, containers, snackbars, etc.) and sets up the default 'Press Start 2P' Google Font.

    You can customize the theme by passing specific Nes theme objects (like NesButtonTheme or NesContainerTheme) or by providing a custom TextTheme.

    MaterialApp(
      theme: flutterNesTheme(
        primaryColor: const Color(0xffb4b6f6),
        brightness: Brightness.light,
        // You can override specific component themes here
        nesButtonTheme: const NesButtonTheme(
          primary: Color(0xff209cee),
          // ... other properties
        ),
      ),
      home: MyHomePage(),
    );
  9. Add custom Theme Extensions to Nes UI

    main

    Nes UI supports Flutter's ThemeExtension mechanism. You can provide your own custom extensions by passing them to the customExtensions argument within the flutterNesTheme() method.

    theme: flutterNesTheme(
      brightness: state.lightMode ? Brightness.light : Brightness.dark,
      customExtensions: [
        CustomExampleExtension.light, // Your custom extension goes here.
      ],
    ),
  10. Configure keyboard key mappings with NesKeyboardKeyMapping

    main

    You can customize which physical keys trigger NES-style input events by providing a NesKeyboardKeyMapping instance to a NesKeyboardInputAdapter. By default, the mapping uses arrow keys for direction, Enter or gameButtonA for confirm, and Escape or gameButtonB for cancel.

    const mapping = NesKeyboardKeyMapping(
      up: [LogicalKeyboardKey.keyW],
      down: [LogicalKeyboardKey.keyS],
      left: [LogicalKeyboardKey.keyA],
      right: [LogicalKeyboardKey.keyD],
      confirm: [LogicalKeyboardKey.space],
      cancel: [LogicalKeyboardKey.backspace],
    );
    
    final adapter = NesKeyboardInputAdapter(mapping: mapping);
  11. Configure NesPalette appearance and behavior

    main

    You can customize the layout and input capabilities of the NesPalette using the following parameters:

    ParameterTypeDefaultDescription
    enableCustomInputbooltrueEnables/disables the hex code text field and the eyedropper icon.
    columnCountint6Sets how many color swatches appear per row.
    sizedouble32Sets the width and height of individual color swatches.
    heightdouble?nullSets a specific height for the scrollable grid area.
    colorsList<Color>N/AThe source list of colors to be rendered in the grid.
  12. Implement a custom NesInputAdapter

    main

    To support new input methods (like a physical NES controller or a gamepad), extend the NesInputAdapter class. You must implement addListener to register callbacks for specific NesInputEvent types on a FocusNode, and disposeListeners to clean up those listeners.

    class MyCustomGamepadAdapter extends NesInputAdapter {
      @override
      void addListener(FocusNode node, NesInputEvent event, VoidCallback callback) {
        // Register your custom logic here
      }
    
      @override
      void disposeListeners(FocusNode node) {
        // Clean up your custom logic here
      }
    }