<|start_filename|>packages/self_storing_input/lib/src/self_storing_radio_group.dart<|end_filename|>
import 'package:flutter/material.dart';
import 'package:self_storing_input/src/self_storing_radio_group/custom_radio.dart';
import 'package:self_storing_input/src/self_storing_radio_group/shared_state.dart';

import 'primitives/overlay.dart';
import 'primitives/saver.dart';
import 'primitives/the_progress_indicator.dart';
import 'self_storing_radio_group/self_storing_radio_group_style.dart';

/// A widget to enter and store a boolean value.
class SelfStoringRadioGroup<K> extends StatefulWidget {
  /// [Saver.validate] will not be invoked for [SelfStoringRadioGroup].
  final Saver<K> saver;

  /// Key of the item to be provided to [saver].
  final K itemKey;
  final OverlayController overlayController;
  final SelfStoringRadioGroupStyle style;

  /// Value to use if the loaded value is not in the list of values or
  /// if all values are unchecked.
  final Object? defaultValue;

  /// If true, the radio buttons in the group can be unselected,
  /// returning to the state when user did not enter a value yet.
  final bool isUnselectable;

  /// Map <value, display name>.
  final Map<Object, String> items;

  SelfStoringRadioGroup(
    this.itemKey, {
    Key? key,
    saver,
    overlayController,
    this.style = const SelfStoringRadioGroupStyle(),
    required this.items,
    this.defaultValue,
    this.isUnselectable = false,
  })  : overlayController = overlayController ?? OverlayController(),
        this.saver = saver ?? NoOpSaver<K>(),
        super(key: key);

  @override
  _SelfStoringRadioGroupState createState() => _SelfStoringRadioGroupState();
}

class _SelfStoringRadioGroupState extends State<SelfStoringRadioGroup> {
  bool _isLoading = true;
  late SharedState _state;

  @override
  void initState() {
    _loadValue();
    super.initState();
  }

  @override
  void dispose() {
    _state.removeListener(_emptySetState);
    widget.overlayController.removeListener(_state.closeOverlay);
    super.dispose();
  }

  void _emptySetState() => setState(() {});

  Future<void> _loadValue() async {
    Object? storedValue = await widget.saver.load<Object>(widget.itemKey);
    if (!widget.items.containsKey(storedValue))
      storedValue = widget.defaultValue;

    _isLoading = false;
    _state = SharedState(
      widget.defaultValue,
      widget.overlayController,
      widget.saver,
      widget.itemKey,
      widget.style,
      storedValue,
      widget.isUnselectable,
    )..addListener(_emptySetState);
    widget.overlayController.addListener(_state.closeOverlay);

    setState(() => {});
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return theProgressIndicator;

    List<Widget> elements = widget.items.keys
        .map((v) => _buildRadioButton(v, widget.items[v] ?? ''))
        .toList(growable: false);
    return Column(
      children: elements,
    );
  }

  Widget _buildRadioButton(Object value, String name) {
    return Row(
      children: [
        CustomRadio(_state, value),
        Flexible(child: Text(name)),
        if (_state.isSaving as bool && _state.pendingValue == value)
          theProgressIndicator,
      ],
    );
  }
}


<|start_filename|>packages/self_storing_input/lib/src/primitives/overlay.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';

/// A controller for the overlay of a widget,
/// a floating dialog box used to
/// edit/save the value and/or notify about errors.
class OverlayController with ChangeNotifier {
  /// Closes the overlay of the controlling widget. If there is no active overlay,
  /// nothing will happen.
  void close() {
    notifyListeners();
  }
}

class OverlayStyle {
  final double elevation;
  final double width;
  final double height;
  final double margin;

  const OverlayStyle.forTextEditor({
    this.elevation = 4.0,
    this.width = 500,
    this.height = 100,
    this.margin = 8,
  });

  const OverlayStyle.forMessage({
    this.elevation = 4.0,
    this.width = 200,
    this.height = 80,
    this.margin = 2,
  });
}


<|start_filename|>packages/flutter_simple_treeview/test/tree_view_test.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';
import 'package:flutter_simple_treeview/flutter_simple_treeview.dart';
import 'package:flutter_test/flutter_test.dart';

/// Wraps widget to MaterialApp and pumps.
Future<void> _wrapAndPump(WidgetTester tester, Widget widget) async {
  var wrapped = MaterialApp(
    home: SingleChildScrollView(
        child: SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Card(child: widget),
    )),
  );
  await tester.pumpWidget(wrapped);
}

void main() {
  group('TreeView', () {
    testWidgets('renders all nodes when expanded.',
        (WidgetTester tester) async {
      var widget = TreeView(
        nodes: [
          TreeNode(content: Text('n0')),
          TreeNode(content: Text('n1')),
          TreeNode(
            content: Text('n2'),
            children: [
              TreeNode(content: Text('n3')),
              TreeNode(content: Text('n4')),
              TreeNode(
                content: Text('n5'),
                children: [
                  TreeNode(content: Text('n6')),
                ],
              ),
            ],
          ),
        ],
      );
      await _wrapAndPump(tester, widget);

      for (var i = 0; i < 7; i++) {
        expect(
          find.byWidgetPredicate(
              (widget) => widget is Text && widget.data == 'n$i'),
          findsOneWidget,
        );
      }
    });

    test('generates unique key if the key is null.', () {
      var tree = TreeView(nodes: [
        TreeNode(),
        TreeNode(
          key: ValueKey(1),
          children: [TreeNode()],
        )
      ]);

      expect(tree.nodes[0].key, isNotNull);
      expect(tree.nodes[1].key, ValueKey(1));
      expect(tree.nodes[0].key == tree.nodes[1].children![0].key, false);
    });

    test('throws if the key is duplicate.', () {
      var nodes = [TreeNode(key: ValueKey(1)), TreeNode(key: ValueKey(1))];

      expect(() => TreeView(nodes: nodes), throwsA(isA<ArgumentError>()));
    });

    test('has immutable node list.', () {
      try {
        TreeView(nodes: []).nodes.add(TreeNode());
      } catch (e) {
        print(e.runtimeType);
      }
      expect(
          () => TreeView(nodes: []).nodes.add(TreeNode()),
          throwsA(
              predicate((dynamic e) => e.toString().contains('unmodifiable'))));
    });
  });
}


<|start_filename|>packages/self_storing_input/lib/self_storing_input.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

export 'src/primitives/operation_result.dart';
export 'src/primitives/overlay.dart';
export 'src/primitives/saver.dart';
export 'src/self_storing_checkbox.dart';
export 'src/self_storing_checkbox/self_storing_checkbox_style.dart';
export 'src/self_storing_radio_group.dart';
export 'src/self_storing_radio_group/self_storing_radio_group_style.dart';
export 'src/self_storing_text.dart';
export 'src/self_storing_text/self_storing_text_style.dart';


<|start_filename|>packages/self_storing_input/lib/src/self_storing_checkbox/self_storing_checkbox_style.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import '../primitives/overlay.dart';

/// A style for [SelfStoringCheckbox].
class SelfStoringCheckboxStyle {
  /// Style of the error message box.
  final OverlayStyle overlayStyle;

  /// Size of the button that closes the error message box.
  final double closeIconSize;

  const SelfStoringCheckboxStyle(
      {this.closeIconSize = 18,
      this.overlayStyle = const OverlayStyle.forMessage()});
}


<|start_filename|>packages/flutter_simple_treeview/lib/src/primitives/tree_controller.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/foundation.dart';

/// A controller for a tree state.
///
/// Allows to modify the state of the tree.
class TreeController {
  bool _allNodesExpanded;
  final Map<Key, bool> _expanded = <Key, bool>{};

  TreeController({allNodesExpanded = true})
      : _allNodesExpanded = allNodesExpanded;

  bool get allNodesExpanded => _allNodesExpanded;

  bool isNodeExpanded(Key key) {
    return _expanded[key] ?? _allNodesExpanded;
  }

  void toggleNodeExpanded(Key key) {
    _expanded[key] = !isNodeExpanded(key);
  }

  void expandAll() {
    _allNodesExpanded = true;
    _expanded.clear();
  }

  void collapseAll() {
    _allNodesExpanded = false;
    _expanded.clear();
  }

  void expandNode(Key key) {
    _expanded[key] = true;
  }

  void collapseNode(Key key) {
    _expanded[key] = false;
  }
}


<|start_filename|>packages/self_storing_input/lib/src/self_storing_checkbox.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';
import 'package:self_storing_input/src/self_storing_checkbox/shared_state.dart';

import 'primitives/overlay.dart';
import 'primitives/saver.dart';
import 'primitives/the_progress_indicator.dart';
import 'self_storing_checkbox/custom_checkbox.dart';
import 'self_storing_checkbox/self_storing_checkbox_style.dart';

/// A widget to enter and store a boolean value.
class SelfStoringCheckbox<K> extends StatefulWidget {
  /// [Saver.validate] will not be invoked for [SelfStoringCheckbox].
  final Saver<K> saver;

  /// Key of the item to be provided to [saver].
  final K itemKey;
  final OverlayController overlayController;
  final SelfStoringCheckboxStyle style;

  /// If true this checkbox's value can be any one of true, false, or null.
  ///
  /// If tristate is false, a null value for this checkbox
  /// will be interpreted as false.
  /// See [Checkbox.tristate] for more.
  final bool tristate;

  /// The same as [CheckboxListTile.title].
  final Widget title;

  SelfStoringCheckbox(
    this.itemKey, {
    Key? key,
    saver,
    overlayController,
    this.tristate = true,
    Widget? title,
    this.style = const SelfStoringCheckboxStyle(),
  })  : overlayController = overlayController ?? OverlayController(),
        this.title = title ?? Container(width: 0, height: 0),
        this.saver = saver ?? NoOpSaver<K>(),
        super(key: key);

  @override
  _SelfStoringCheckboxState createState() => _SelfStoringCheckboxState();
}

class _SelfStoringCheckboxState extends State<SelfStoringCheckbox> {
  bool _isLoading = true;
  late SharedState _state;

  @override
  void initState() {
    _loadValue();
    super.initState();
  }

  @override
  void dispose() {
    _state.removeListener(_onSharedStateChange);
    super.dispose();
  }

  void _onSharedStateChange() {
    setState(() {});
  }

  Future<void> _loadValue() async {
    var storedValue = await widget.saver.load<bool>(widget.itemKey);
    if (storedValue == null && !widget.tristate) storedValue = false;
    _state = SharedState(
      saver: widget.saver,
      itemKey: widget.itemKey,
      overlayController: widget.overlayController,
      style: widget.style,
      tristate: widget.tristate,
      storedValue: storedValue,
    )..addListener(_onSharedStateChange);
    _isLoading = false;
    setState(() => {});
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return theProgressIndicator;

    return Row(
      children: [
        CustomCheckbox(_state),
        widget.title,
        if (_state.isSaving) theProgressIndicator,
      ],
    );
  }
}


<|start_filename|>packages/flutter_simple_treeview/lib/src/tree_view.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';

import 'builder.dart';
import 'copy_tree_nodes.dart';
import 'primitives/tree_controller.dart';
import 'primitives/tree_node.dart';

/// Tree view with collapsible and expandable nodes.
class TreeView extends StatefulWidget {
  /// List of root level tree nodes.
  final List<TreeNode> nodes;

  /// Horizontal indent between levels.
  final double? indent;

  /// Size of the expand/collapse icon.
  final double? iconSize;

  /// Tree controller to manage the tree state.
  final TreeController? treeController;

  TreeView(
      {Key? key,
      required List<TreeNode> nodes,
      this.indent = 40,
      this.iconSize,
      this.treeController})
      : nodes = copyTreeNodes(nodes),
        super(key: key);

  @override
  _TreeViewState createState() => _TreeViewState();
}

class _TreeViewState extends State<TreeView> {
  TreeController? _controller;

  @override
  void initState() {
    _controller = widget.treeController ?? TreeController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return buildNodes(
        widget.nodes, widget.indent, _controller!, widget.iconSize);
  }
}


<|start_filename|>packages/self_storing_input/test/self_storing_checkbox_test.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter_test/flutter_test.dart';
import 'package:self_storing_input/self_storing_input.dart';

import 'testing/widget_testing.dart';

void main() {
  group('SelfStoringCheckbox', () {
    testWidgets('widget renders successfully if no parameters provided.',
        (WidgetTester tester) async {
      await wrapAndPump(tester, SelfStoringCheckbox('id'));
    });
  });
}


<|start_filename|>packages/flutter_simple_treeview/lib/flutter_simple_treeview.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

export 'src/primitives/tree_controller.dart';
export 'src/primitives/tree_node.dart';
export 'src/tree_view.dart';


<|start_filename|>packages/self_storing_input/lib/src/primitives/saver.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'operation_result.dart';

/// Loads and saves a value of an input widget.
abstract class Saver<K> {
  /// Loads the value  of a data item from the storage.
  ///
  /// [itemKey] identifies the data item. [itemKey] can be of any form:
  /// resource url, guid, tuple <db, table, row, column> etc.
  Future<T?> load<T>(K itemKey);

  /// Saves a value to the storage by [itemKey].
  ///
  /// See [load] for [itemKey].
  Future<OperationResult> save<T>(K itemKey, T? value);

  /// Validates whether [value] is allowed to be stored for [itemKey].
  /// Disallowed values will not be passed to [save].
  ///
  /// Use this method only for synchronous validation,
  /// as asynchronous validation should be done in [save].
  ///
  /// See [load] for [itemKey].
  OperationResult validate<T>(K itemKey, T? value);
}

/// Trivial implementation of [Saver]. Always returns null value and
/// successful operation result.
class NoOpSaver<K> implements Saver<K> {
  const NoOpSaver();

  @override
  Future<T?> load<T>(K itemKey) async => null;

  @override
  OperationResult validate<T>(K itemKey, T? value) => OperationResult.success();

  @override
  Future<OperationResult> save<T>(K itemKey, T? value) async =>
      OperationResult.success();
}


<|start_filename|>packages/self_storing_input/lib/src/self_storing_radio_group/custom_radio.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';
import 'package:self_storing_input/src/primitives/message_overlay.dart';

import '../primitives/overlay_builder.dart';
import 'shared_state.dart';

/// Radio button that saves the radio group value using [state.saver]
/// and can be unchecked.
class CustomRadio extends StatelessWidget {
  final SharedState? state;
  final Object value;

  const CustomRadio(this.state, this.value);

  bool get isSelected => state!.selectedValue == value;

  @override
  Widget build(BuildContext context) {
    var isEnabled = !(state!.isSaving as bool) && state!.overlay == null;
    var iconSize = (Theme.of(context).iconTheme.size ?? 24);
    var icon = Icons.radio_button_unchecked;
    if (isSelected) {
      icon = state!.isUnselectable
          ? Icons.check_circle
          : Icons.radio_button_checked;
    }

    return GestureDetector(
      onTap: isEnabled ? () => _onTap(context) : null,
      child: Padding(
        padding: EdgeInsets.all(iconSize / 2),
        child: Icon(
          icon,
          color: isSelected && isEnabled
              ? Theme.of(context).accentColor
              : Theme.of(context).disabledColor,
        ),
      ),
    );
  }

  Future<void> _onTap(BuildContext context) async {
    if (!state!.isUnselectable && isSelected) return;
    await state!.select(value, !isSelected);
    if (!state!.operationResult.isSuccess) _showOverlay(context);
  }

  void _showOverlay(BuildContext context) {
    state!.closeOverlay();
    state!.overlay = _buildOverlay(context);
    Overlay.of(context)!.insert(state!.overlay!);
  }

  OverlayEntry _buildOverlay(BuildContext context) {
    return createOverlayInTheMiddle(
      MessageOverlay(
        message: state!.operationResult.error!,
        style: state!.style.overlayStyle,
        closeIconSize: state!.style.closeIconSize,
        overlayController: state!.overlayController,
      ),
      context,
      state!.style.overlayStyle,
    );
  }
}


<|start_filename|>packages/self_storing_input/test/testing/widget_testing.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

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

/// Wraps [widget] to MaterialApp and pumps.
Future<void> wrapAndPump(WidgetTester tester, Widget widget) async {
  var wrapped = MaterialApp(
    home: SingleChildScrollView(
        child: SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Card(child: widget),
    )),
  );
  await tester.pumpWidget(wrapped);
}

/// Wraps widget into sized and aligned container.
Widget sizeAndLayout(Widget child) => SizedBox(
      width: 5000,
      height: 5000,
      child: Align(alignment: Alignment.topLeft, child: child),
    );


<|start_filename|>packages/visibility_detector/test/text_handle_test.dart<|end_filename|>
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:visibility_detector/visibility_detector.dart';

void main() {
  final visibilityDetectorKey = UniqueKey();

  // Disable VisibilityDetector timers.
  VisibilityDetectorController.instance.updateInterval = Duration.zero;

  testWidgets(
    'VisibilityDetector does not affect the positioning of text selection '
    'handles',
    (WidgetTester tester) async {
      final expectedSelectionHandles = await _setUpSelectionHandles(
        tester,
        null,
      );

      final actualSelectionHandles = await _setUpSelectionHandles(
        tester,
        (textField) => VisibilityDetector(
          key: visibilityDetectorKey,
          onVisibilityChanged: (visibilityInfo) {},
          child: textField,
        ),
      );

      expect(actualSelectionHandles, expectedSelectionHandles);
    },
  );
}

/// Sets up a [TextField] with sample text and a sample selection.
///
/// [wrapTextField] is a function that takes the [TextField] as an argument and
/// returns it wrapped in another widget.  If [wrapTextField] is null, the
/// [TextField] will be added to the widget tree directly.
///
/// Returns the bounding rectangles (in global coordinates).  The returned
/// [List] will have 3 elements:
/// 0: The [Rect] of the [TextField].
/// 1: The [Rect] of the starting selection handle.
/// 2: The [Rect] of the ending selection handle.
Future<List<Rect>> _setUpSelectionHandles(
  WidgetTester tester,
  Widget Function(Widget)? wrapTextField,
) async {
  final textController = TextEditingController()
    ..text = 'The five boxing wizards jump quickly';
  final textField = TextField(controller: textController);

  await tester.pumpWidget(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: (wrapTextField ?? (x) => x)(textField),
      ),
    ),
  );

  // The [TextField] must be focused first.  Otherwise any changes we make
  // to the selection will be lost when it gains focus.
  final textFieldFinder = find.byType(TextField);
  await tester.tap(textFieldFinder);

  const selection = TextSelection(baseOffset: 4, extentOffset: 10);
  textController.selection = selection;
  await tester.pumpAndSettle();

  final state = tester.state<EditableTextState>(find.byType(EditableText));
  expect(state.selectionOverlay!.handlesAreVisible, true);

  // Find the text selection handles (via [CustomPaint] widgets within
  // [CompositedTransformFollower] parents).
  const handleCount = 2;
  final selectionHandles = find.descendant(
    of: find.byType(CompositedTransformFollower),
    matching: find.byType(CustomPaint),
  );
  expect(selectionHandles, findsNWidgets(handleCount));

  final selectionHandleRects = [
    for (var i = 0; i < handleCount; i += 1)
      tester.getRect(selectionHandles.at(i)),
  ];

  expect(selectionHandleRects[0], isNot(equals(selectionHandleRects[1])));
  return [tester.getRect(textFieldFinder), ...selectionHandleRects];
}


<|start_filename|>packages/self_storing_input/lib/src/self_storing_text/shared_state.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';

import '../primitives/overlay.dart';
import '../primitives/saver.dart';
import 'self_storing_text_style.dart';

/// State that needs to be shared between [OverlayBox] and its
/// parent.
class SharedState with ChangeNotifier {
  String? _storedValue;
  final OverlayController overlayController;
  final Saver saver;
  final Object itemKey;
  final SelfStoringTextStyle style;

  String? get storedValue => _storedValue;
  set storedValue(String? value) {
    if (value == _storedValue) return;
    _storedValue = value;
    notifyListeners();
  }

  SharedState({
    storedValue,
    required this.overlayController,
    required this.saver,
    required this.itemKey,
    required this.style,
  }) : _storedValue = storedValue;
}


<|start_filename|>packages/flutter_simple_treeview/test/primitives/tree_controller_test.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

import 'package:flutter/material.dart';
import 'package:flutter_simple_treeview/src/primitives/tree_controller.dart';
import 'package:test/test.dart';

void main() {
  Key k1 = UniqueKey();
  Key k2 = UniqueKey();

  test('Initial state is equal to default state.', () {
    for (var allExpanded in [true, false]) {
      var state = TreeController(allNodesExpanded: allExpanded);
      expect(state.isNodeExpanded(k1), allExpanded);
    }
  });

  test('Toggle works.', () {
    for (var allExpanded in [true, false]) {
      var state = TreeController(allNodesExpanded: allExpanded);
      state.toggleNodeExpanded(k1);
      expect(state.isNodeExpanded(k1), !allExpanded);
      state.toggleNodeExpanded(k1);
      expect(state.isNodeExpanded(k1), allExpanded);
    }
  });

  test('States are independant.', () {
    for (var allExpanded in [true, false]) {
      var state = TreeController(allNodesExpanded: allExpanded);
      state.toggleNodeExpanded(k1);
      expect(state.isNodeExpanded(k2), allExpanded);
      state.toggleNodeExpanded(k1);
      expect(state.isNodeExpanded(k2), allExpanded);
      state.toggleNodeExpanded(k2);
      expect(state.isNodeExpanded(k1), allExpanded);
    }
  });
}


<|start_filename|>packages/self_storing_input/lib/src/primitives/operation_result.dart<|end_filename|>
// Copyright 2020 the Dart project authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

/// Result of value validation.
class OperationResult {
  bool get isSuccess => error == null;
  final String? error;

  const OperationResult.success() : error = null;

  const OperationResult.error(this.error);
}
