Skip to content

Commit

Permalink
Reordering method and callback in controller. #21
Browse files Browse the repository at this point in the history
  • Loading branch information
caduandrade committed Apr 29, 2023
1 parent a4b5fdb commit 31b8c4a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 5 deletions.
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.16.0+1"
version: "1.17.0"
term_glyph:
dependency: transitive
description:
Expand Down
44 changes: 41 additions & 3 deletions lib/src/tabbed_view_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import 'dart:collection';
import 'package:flutter/widgets.dart';
import 'package:tabbed_view/src/tab_data.dart';

/// Event that will be triggered when the tab is reorder.
typedef OnReorder = void Function(int oldIndex, int newIndex);

/// The [TabbedView] controller.
///
/// Stores tabs and selection tab index.
Expand All @@ -11,11 +14,11 @@ import 'package:tabbed_view/src/tab_data.dart';
///
/// Remember to dispose of the [TabbedView] when it is no longer needed. This will ensure we discard any resources used by the object.
class TabbedViewController extends ChangeNotifier {
factory TabbedViewController(List<TabData> tabs) {
return TabbedViewController._(tabs);
factory TabbedViewController(List<TabData> tabs, {OnReorder? onReorder}) {
return TabbedViewController._(tabs, onReorder);
}

TabbedViewController._(this._tabs) {
TabbedViewController._(this._tabs, this.onReorder) {
if (_tabs.length > 0) {
_selectedIndex = 0;
}
Expand All @@ -28,6 +31,8 @@ class TabbedViewController extends ChangeNotifier {

int? _selectedIndex;

final OnReorder? onReorder;

UnmodifiableListView<TabData> get tabs => UnmodifiableListView(_tabs);

/// The selected tab index
Expand All @@ -42,6 +47,39 @@ class TabbedViewController extends ChangeNotifier {
notifyListeners();
}

/// Gets the selected tab.
TabData? get selectedTab =>
_selectedIndex != null ? _tabs[_selectedIndex!] : null;

/// Reorders a tab.
void reorderTab(int oldIndex, int newIndex) {
if (_tabs.isEmpty) {
throw ArgumentError('There are no tabs.');
}
if (oldIndex < 0 || oldIndex >= _tabs.length) {
throw ArgumentError('Index out of range.', 'oldIndex');
}
if (newIndex < 0 || newIndex >= _tabs.length) {
throw ArgumentError('Index out of range.', 'newIndex');
}
if (oldIndex != newIndex) {
TabData? selectedTab;
if (_selectedIndex != null) {
selectedTab = _tabs[_selectedIndex!];
}

TabData tab = _tabs.removeAt(oldIndex);
_tabs.insert(newIndex, tab);
if (selectedTab != null) {
_selectedIndex = _tabs.indexOf(selectedTab);
}
notifyListeners();
if (onReorder != null) {
onReorder!(oldIndex, newIndex);
}
}
}

/// Inserts [TabData] at position [index] in the [tabs].
///
/// The [index] value must be non-negative and no greater than [tabs.length].
Expand Down
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,5 @@ packages:
source: hosted
version: "2.1.4"
sdks:
dart: ">=2.18.0 <4.0.0"
dart: ">=2.18.0 <3.0.0"
flutter: ">=1.17.0"
96 changes: 96 additions & 0 deletions test/reorder_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tabbed_view/src/tab_data.dart';
import 'package:tabbed_view/src/tabbed_view_controller.dart';

void main() {
group('controller', () {
test('reorder', () {
int reorderCount = 0;
OnReorder onReorder = (int oldIndex, int newIndex) {
reorderCount++;
};

// initial order a,b,c,d
TabbedViewController controller = TabbedViewController([
TabData(text: 'a'),
TabData(text: 'b'),
TabData(text: 'c'),
TabData(text: 'd')
], onReorder: onReorder);

expect(false, controller.tabs.isEmpty);
expect(4, controller.tabs.length);

expect('a', controller.tabs[0].text);
expect('b', controller.tabs[1].text);
expect('c', controller.tabs[2].text);
expect('d', controller.tabs[3].text);
expect('a', controller.selectedTab?.text);

// ignores
controller.reorderTab(0, 0);
expect(0, reorderCount);
expect('a', controller.tabs[0].text);
expect('b', controller.tabs[1].text);
expect('c', controller.tabs[2].text);
expect('d', controller.tabs[3].text);
expect('a', controller.selectedTab?.text);

// a,b,c,d => b,a,c,d
controller.reorderTab(0, 1);
expect(1, reorderCount);
expect('b', controller.tabs[0].text);
expect('a', controller.tabs[1].text);
expect('c', controller.tabs[2].text);
expect('d', controller.tabs[3].text);
expect('a', controller.selectedTab?.text);

// b,a,c,d => a,b,c,d
controller.reorderTab(1, 0);
expect(2, reorderCount);
expect('a', controller.tabs[0].text);
expect('b', controller.tabs[1].text);
expect('c', controller.tabs[2].text);
expect('d', controller.tabs[3].text);
expect('a', controller.selectedTab?.text);

// a,b,c,d => a,c,d,b
controller.reorderTab(1, 3);
expect(3, reorderCount);
expect('a', controller.tabs[0].text);
expect('c', controller.tabs[1].text);
expect('d', controller.tabs[2].text);
expect('b', controller.tabs[3].text);
expect('a', controller.selectedTab?.text);

controller.selectedIndex = 1;
expect('c', controller.selectedTab?.text);

// a,c,d,b => c,d,b,a
controller.reorderTab(0, 3);
expect(4, reorderCount);
expect('c', controller.tabs[0].text);
expect('d', controller.tabs[1].text);
expect('b', controller.tabs[2].text);
expect('a', controller.tabs[3].text);
expect('c', controller.selectedTab?.text);

expect(() => controller.reorderTab(-1, 1), indexOutOfRangeError());
expect(() => controller.reorderTab(1, 100), indexOutOfRangeError());
expect(() => controller.reorderTab(-1, 100), indexOutOfRangeError());

controller = TabbedViewController([]);
expect(() => controller.reorderTab(1, 2), emptyError());
});
});
}

Matcher indexOutOfRangeError() {
return throwsA(predicate(
(x) => x is ArgumentError && x.message == 'Index out of range.'));
}

Matcher emptyError() {
return throwsA(predicate(
(x) => x is ArgumentError && x.message == 'There are no tabs.'));
}

0 comments on commit 31b8c4a

Please sign in to comment.