Flutter Fortune Wheel

repository·main·Indexed 42 days ago

https://github.com/kevlatus/flutter_fortune_wheel

A highly customizable Flutter package for visualizing random selection processes using circular wheels (FortuneWheel) or horizontal bars (FortuneBar). It supports mobile, desktop, and web, featuring customizable item weights, styling strategies, haptic feedback, and a comprehensive set of gesture callbacks for individual segments.

Tokens
4.7K
Snippets
18
Records
20
Agent score
53%

What's inside Flutter Fortune Wheel

  1. Install and use Flutter Fortune Wheel

    main

    To use the package, install it via pub.dev. You can then use the FortuneWheel widget to create a circular wheel of fortune. It requires a Stream<int> for the selected property to control the animation and a list of FortuneItem objects for the content.

    import 'package:flutter/material.dart';
    import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart';
    
    StreamController<int> controller = StreamController<int>();
    FortuneWheel(
      selected: controller.stream,
      items: [
        FortuneItem(child: Text('Han Solo')),
        FortuneItem(child: Text('Yoda')),
        FortuneItem(child: Text('Obi-Wan Kenobi')),
      ],
    )
  2. Customize iOS launch screen assets

    main

    To change the image used for the iOS launch screen, you can either replace the image files directly in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory or use Xcode for a more visual approach.

    Using Xcode:

    1. Open the iOS project in Xcode by running open ios/Runner.xcworkspace from your terminal.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog to replace the existing launch images.
    open ios/Runner.xcworkspace
  3. How FortuneBar handles item weights and sizing

    main

    FortuneBar uses the weight property of each FortuneItem to determine its width relative to other items.

    1. Total Weight: The sum of all item.weight values.
    2. Unit Width: The width of a single 'weight unit' is calculated as totalWidth / totalWeight.
    3. Item Width: Each item's width is item.weight * unitWidth.
    4. Visible Items: The visibleItemCount property determines how many 'average weight' slots are visible in the viewport at once.
  4. Use StyleStrategy to style multiple FortuneItems

    main

    Instead of styling items individually, you can use a StyleStrategy to provide a consistent or algorithmic look to the entire wheel. A strategy implements getItemStyle(ThemeData theme, int index, int itemCount), allowing the style to change based on the item's position or the total number of items.

    Available Strategies

    1. UniformStyleStrategy

    Applies the same style to every item. It uses provided properties or falls back to the current ThemeData (using primaryColor for borders and a 0.3 opacity version of the primary color for backgrounds).

    2. AlternatingStyleStrategy

    Creates an alternating pattern using the ThemeData.primaryColor. Even-indexed items are rendered at 0.5 opacity, and odd-indexed items use the full color. If the total item count is odd, the first item is adjusted to 0.75 opacity to maintain visual balance.

    Handling Disabled Items

    Both strategies can implement DisableAwareStyleStrategy by providing a list of disabledIndices. When an index is in this list, the strategy will return a disabled style instead of the regular one.

    // Example using UniformStyleStrategy
    const strategy = UniformStyleStrategy(
      color: Colors.red,
      disabledIndices: [2, 5],
    );
    
    // Example using AlternatingStyleStrategy
    const strategy = AlternatingStyleStrategy(
      disabledIndices: [1],
    );
  5. Use FortuneBar for vertical space efficiency

    main

    If vertical screen space is limited, use the FortuneBar widget instead of FortuneWheel. It provides a horizontal bar-style visualization of the selection process.

    import 'package:flutter/material.dart';
    import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart';
    
    StreamController<int> controller = StreamController<int>();
    FortuneBar(
      selected: controller.stream,
      items: [
        FortuneItem(child: Text('Han Solo')),
        FortuneItem(child: Text('Yoda')),
        FortuneItem(child: Text('Obi-Wan Kenobi')),
      ],
    )
  6. Style FortuneItems and use StyleStrategy

    main

    You can style items in two ways:

    1. Individual Styling: Pass a FortuneItemStyle to the style property of a specific FortuneItem.
    2. Common Styling: Use the styleStrategy property on the parent widget (e.g., FortuneWheel or FortuneBar) to apply a logic to all items.
      • FortuneWheel defaults to AlternatingStyleStrategy().
      • FortuneBar defaults to UniformStyleStrategy().
      • You can provide custom implementations of StyleStrategy to extend this.
    // styling FortuneItems individually
    FortuneWheel(
      selected: Stream.value(0),
      items: [
        FortuneItem(
          child: Text('A'),
          style: FortuneItemStyle(
            color: Colors.red, // <-- custom circle slice fill color
            borderColor: Colors.green, // <-- custom circle slice stroke color
            borderWidth: 3, // <-- custom circle slice stroke width
          ),
        ),
        FortuneItem(child: Text('B')),
      ],
    )
    
    // common styling for all items of a FortuneWidget
    FortuneBar(
      // using alternating item styles on a fortune bar
      styleStrategy: AlternatingStyleStrategy(),
      selected: Stream.value(0),
      items: [
        FortuneItem(child: Text('Han Solo')),
        FortuneItem(child: Text('Yoda')),
        FortuneItem(child: Text('Obi-Wan Kenobi')),
      ],
    )
  7. Customize indicators with FortuneIndicator

    main

    Indicators mark the selected position. You can customize them by passing a list of FortuneIndicator objects to the indicators property of a FortuneWidget.

    • Default Wheel Indicator: TriangleIndicator.
    • Default Bar Indicator: RectangleIndicator.

    Each FortuneIndicator allows you to specify an alignment and a child (which can be a styled version of an existing indicator or a completely custom widget).

    FortuneWheel(
      selected: Stream.value(0),
      indicators: <FortuneIndicator>[
        FortuneIndicator(
          alignment: Alignment.bottomCenter, // <-- changing the position of the indicator
          child: TriangleIndicator(
            color: Colors.green, // <-- changing the color of the indicator
            width: 20.0, // <-- changing the width of the indicator
            height: 20.0, // <-- changing the height of the indicator
            elevation: 0, // <-- changing the elevation of the indicator
          ),
        ),
      ],
      items: [
        FortuneItem(child: Text('A')),
        FortuneItem(child: Text('B')),
      ],
    )
  8. Customize drag behavior with PanPhysics

    main

    The physics property allows you to control how the widget reacts to touch and drag input. It expects an implementation of the PanPhysics class.

    • Disable dragging: Pass NoPanPhysics().
    • FortuneWheel recommendation: Use CircularPanPhysics().
    • FortuneBar recommendation: Uses DirectionalPanPhysics.horizontal by default.

    You can also use the onFling callback to trigger a new selection when a user performs a fling gesture.

    StreamController<int> controller = StreamController<int>();
    FortuneWheel(
      // changing the return animation when the user stops dragging
      physics: CircularPanPhysics(
        duration: Duration(seconds: 1),
        curve: Curves.decelerate,
      ),
      onFling: () {
        controller.add(1);
      },
      selected: controller.stream,
      items: [
        FortuneItem(child: Text('Han Solo')),
        FortuneItem(child: Text('Yoda')),
        FortuneItem(child: Text('Obi-Wan Kenobi')),
      ],
    )
  9. Configure PanPhysics and Drag Behavior

    main

    The FortuneWheel supports interactive dragging and flinging. You can customize the physics of the pan interaction using the physics property (defaults to CircularPanPhysics) and handle fling events via the onFling callback.

    FortuneWheel(
      items: items,
      physics: myCustomPanPhysics,
      onFling: () => print('User flung the wheel!'),
    )
  10. Implement a custom StyleStrategy

    main

    To create a custom visual pattern for your wheel, implement the StyleStrategy interface. This allows you to calculate a unique FortuneItemStyle for every item based on its index and the total count.

    class MyCustomStrategy implements StyleStrategy {
      @override
      FortuneItemStyle getItemStyle(ThemeData theme, int index, int itemCount) {
        return FortuneItemStyle(
          color: index % 2 == 0 ? Colors.red : Colors.blue,
          textStyle: TextStyle(color: Colors.white),
        );
      }
    }
  11. Configure FortuneWheel indicators

    main

    You can customize the visual markers (like arrows or triangles) that point to the selected item using the indicators property. By default, it uses a single TriangleIndicator aligned to Alignment.topCenter. Each indicator in the list can have its own alignment and child widget.

    FortuneWheel(
      items: items,
      indicators: [
        FortuneIndicator(
          alignment: Alignment.topCenter,
          child: TriangleIndicator(),
        ),
      ],
    )
  12. Use the FortuneWheel widget

    main

    The FortuneWheel widget is the primary entrypoint for displaying an interactive spinning wheel. It visualizes a selection process by dividing a wheel into uniformly sized slices based on the provided items. The wheel can be controlled via a Stream<int> of selected indices and supports custom rotation counts, durations, and animation curves.

    FortuneWheel(
      items: [ ... ],
      selected: myStream,
      rotationCount: 5,
      duration: Duration(seconds: 5),
      curve: Curves.easeInOut,
    )