mirror of
https://github.com/feeddeck/feeddeck.git
synced 2026-04-28 18:38:34 -05:00
The index was not reset in the `DeckLayoutSmall` widget, when the user selected a new deck in the settings widget. This was caused because the `DefaultTabController` was not rebuild after a new deck was selected, so that the `initialIndex` value was not used. This is now fixed by adding a `key` to the `DefaultTabController`, which corresponds to the selected deck. This means if the user selects a new deck in the settings the widget will be rebuild and the initial selected tab will be the first one. If a user selects the same deck or switches between the small and large layout the tab will be the formerly selected one.
278 lines
10 KiB
Dart
278 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:feeddeck/models/source.dart';
|
|
import 'package:feeddeck/repositories/app_repository.dart';
|
|
import 'package:feeddeck/repositories/layout_repository.dart';
|
|
import 'package:feeddeck/utils/constants.dart';
|
|
import 'package:feeddeck/widgets/column/column_layout.dart';
|
|
import 'package:feeddeck/widgets/column/create/create_column.dart';
|
|
import 'package:feeddeck/widgets/settings/settings.dart';
|
|
import 'package:feeddeck/widgets/source/source_icon.dart';
|
|
|
|
/// The [DeckLayoutSmall] implements the deck screen via tabs for screens
|
|
/// smaller then our defined breakpoint. It displayes all columns in a tab bar
|
|
/// and when the user clicks on an item in the tab bar it will update the tab
|
|
/// view to the corresponding column.
|
|
///
|
|
/// The tab bar also has two additional items, one to add a new column to the
|
|
/// current deck and one to go to the settings screen of the app.
|
|
class DeckLayoutSmall extends StatelessWidget {
|
|
const DeckLayoutSmall({super.key});
|
|
|
|
/// [_getInitialIndex] returns the initial index for the
|
|
/// [DefaultTabController]. The intial index is saved in the
|
|
/// [LayoutRepository] so that we can use it when a user switches between the
|
|
/// small and large layout or between decks. To not run into errors we have to
|
|
/// check that a column which should be used was not already deleted by a
|
|
/// user before returning the index. If a column was deleted we reset the
|
|
/// index to 0.
|
|
int _getInitialIndex(BuildContext context, int columnsLength) {
|
|
final deckLayoutSmallInitialTabIndex = Provider.of<LayoutRepository>(
|
|
context,
|
|
listen: false,
|
|
).deckLayoutSmallInitialTabIndex;
|
|
|
|
if (deckLayoutSmallInitialTabIndex >= columnsLength) {
|
|
Provider.of<LayoutRepository>(
|
|
context,
|
|
listen: false,
|
|
).deckLayoutSmallInitialTabIndex = 0;
|
|
return 0;
|
|
}
|
|
|
|
return deckLayoutSmallInitialTabIndex;
|
|
}
|
|
|
|
/// [_buildTabs] returns all items for the tab bar. The items are generated
|
|
/// by looping thorugh the `columns` defined in the [AppRepository].
|
|
///
|
|
/// Each item in the tab bar will have an `icon` and an `title`. The icon will
|
|
/// be the icon for the first source in the column and the title will be the
|
|
/// column name.
|
|
List<Tab> _buildTabs(BuildContext context) {
|
|
AppRepository app = Provider.of<AppRepository>(context, listen: false);
|
|
|
|
final List<Tab> widgets = [];
|
|
for (var column in app.columns) {
|
|
widgets.add(
|
|
Tab(
|
|
key: ValueKey(column.id),
|
|
height: 56,
|
|
icon: SourceIcon(
|
|
type: column.sources.isNotEmpty
|
|
? column.sources[0].type
|
|
: FDSourceType.none,
|
|
icon: column.sources.isNotEmpty ? column.sources[0].icon : null,
|
|
size: 24,
|
|
),
|
|
iconMargin: const EdgeInsets.only(bottom: 0),
|
|
child: Container(
|
|
constraints: const BoxConstraints(
|
|
minWidth: 54,
|
|
maxWidth: 54,
|
|
),
|
|
child: Text(
|
|
Characters(column.name)
|
|
.replaceAll(
|
|
Characters(''),
|
|
Characters('\u{200B}'),
|
|
)
|
|
.toString(),
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(
|
|
fontSize: 10,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return widgets;
|
|
}
|
|
|
|
/// [_buildViews] returns all the view for the tab view. The view for each
|
|
/// column is implemented in the [ColumnLayout] widget.
|
|
Widget _buildViews(BuildContext context) {
|
|
AppRepository app = Provider.of<AppRepository>(context, listen: false);
|
|
|
|
if (app.columns.isEmpty) {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(Constants.spacingMiddle),
|
|
child: RichText(
|
|
textAlign: TextAlign.center,
|
|
text: const TextSpan(
|
|
style: TextStyle(
|
|
color: Constants.onSurface,
|
|
fontSize: 14.0,
|
|
),
|
|
children: [
|
|
TextSpan(
|
|
text: 'Add you first column by clicking on the plus icon (',
|
|
),
|
|
WidgetSpan(
|
|
child: Icon(Icons.add, size: 14.0),
|
|
),
|
|
TextSpan(
|
|
text: ') in the tab bar on the bottom.',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
final List<Widget> widgets = [];
|
|
for (var column in app.columns) {
|
|
widgets.add(
|
|
ColumnLayout(
|
|
key: ValueKey(column.id),
|
|
column: column,
|
|
openDrawer: null,
|
|
),
|
|
);
|
|
}
|
|
|
|
return TabBarView(
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
children: widgets,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
AppRepository app = Provider.of<AppRepository>(context, listen: true);
|
|
|
|
return DefaultTabController(
|
|
key: ValueKey(app.activeDeckId),
|
|
initialIndex: _getInitialIndex(context, app.columns.length),
|
|
length: app.columns.length,
|
|
child: Scaffold(
|
|
bottomNavigationBar: SafeArea(
|
|
child: Container(
|
|
decoration: const BoxDecoration(
|
|
border: Border(
|
|
top: BorderSide(
|
|
color: Constants.dividerColor,
|
|
width: 1,
|
|
),
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Theme(
|
|
data: Theme.of(context).copyWith(
|
|
colorScheme: Theme.of(context).colorScheme.copyWith(
|
|
surfaceVariant: Colors.transparent,
|
|
),
|
|
),
|
|
child: TabBar(
|
|
isScrollable: true,
|
|
tabAlignment: TabAlignment.start,
|
|
onTap: (int index) {
|
|
/// When the user clicks on a tab we update the index in
|
|
/// the [LayoutRepository] so that we can use it as
|
|
/// initial index when the widget is rebuild (e.g. when
|
|
/// a user switches between the large and small layout).
|
|
Provider.of<LayoutRepository>(
|
|
context,
|
|
listen: false,
|
|
).deckLayoutSmallInitialTabIndex = index;
|
|
},
|
|
tabs: _buildTabs(context),
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.only(
|
|
left: Constants.spacingSmall,
|
|
right: Constants.spacingSmall,
|
|
),
|
|
decoration: const BoxDecoration(
|
|
border: Border(
|
|
left: BorderSide(color: Constants.dividerColor),
|
|
),
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.add,
|
|
color: Constants.onSecondary,
|
|
),
|
|
onPressed: () {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
isDismissible: false,
|
|
useSafeArea: true,
|
|
backgroundColor: Colors.transparent,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(
|
|
top: Radius.circular(
|
|
Constants.spacingMiddle,
|
|
),
|
|
),
|
|
),
|
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
|
constraints: const BoxConstraints(
|
|
maxWidth: Constants.centeredFormMaxWidth,
|
|
),
|
|
builder: (BuildContext context) {
|
|
return const CreateColumn();
|
|
},
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.only(
|
|
right: Constants.spacingSmall,
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.settings,
|
|
color: Constants.onSecondary,
|
|
),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (BuildContext context) =>
|
|
const Settings(),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
body: SafeArea(
|
|
child: _buildViews(context),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|