feat(app): the bibliography at the foot of the side bar on the desktop

As on a phone's drawer: Bibliography at the end of the topics' foot,
across from New sub-studyset, instead of a button above the content. One
rail serves both now, the drawer and the side bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-08 22:13:34 +02:00
commit fc2f55046b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 43 additions and 34 deletions

View file

@ -244,40 +244,28 @@ class _StudysetViewState() extends State<_StudysetView> {
final all = widget.all; final all = widget.all;
final theme = Theme.of(context); final theme = Theme.of(context);
final wide = context.windowSize.isAtLeastExpanded; final wide = context.windowSize.isAtLeastExpanded;
// The topics, with the bibliography at their foot across from New
// sub-studyset: in the side bar from expanded windows on, below them in
// the drawer.
final rail = StudysetRail( final rail = StudysetRail(
parent: studyset, parent: studyset,
all: all, all: all,
onAddMaterial: _addMaterial.open, onAddMaterial: _addMaterial.open,
footEnd: _BibliographyButton(studyset: studyset),
); );
final content = _Content(studysetId: studyset.id); final content = _Content(studysetId: studyset.id);
final description = studyset.content.description.isBlank final description = studyset.content.description.isBlank
? null ? null
: _Description(studyset: studyset); : _Description(studyset: studyset);
final canGoBack = context.canPop(); final canGoBack = context.canPop();
final bibliography = Align(
alignment: AlignmentDirectional.centerEnd,
child: _BibliographyButton(studyset: studyset),
);
return ScrollHidingFab( return ScrollHidingFab(
fab: wide fab: wide
? null ? null
: _StudyBar(studyset: studyset, addMaterial: _addMaterial), : _StudyBar(studyset: studyset, addMaterial: _addMaterial),
builder: (context, fab) => Scaffold( builder: (context, fab) => Scaffold(
backgroundColor: theme.colorScheme.surfaceContainerLow, backgroundColor: theme.colorScheme.surfaceContainerLow,
// The topics' home below expanded windows, the bibliography at its // The topics' home below expanded windows.
// foot, across from New sub-studyset. drawer: wide ? null : Drawer(child: SafeArea(child: rail)),
drawer: wide
? null
: Drawer(
child: SafeArea(
child: StudysetRail(
parent: studyset,
all: all,
onAddMaterial: _addMaterial.open,
footEnd: _BibliographyButton(studyset: studyset),
),
),
),
appBar: AppBar( appBar: AppBar(
// A drawer would take the back button's place; the bar at the // A drawer would take the back button's place; the bar at the
// bottom opens the topics instead. // bottom opens the topics instead.
@ -365,12 +353,6 @@ class _StudysetViewState() extends State<_StudysetView> {
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
bibliography,
const SizedBox(height: 12),
Expanded( Expanded(
child: SectionCard( child: SectionCard(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -379,9 +361,6 @@ class _StudysetViewState() extends State<_StudysetView> {
), ),
], ],
), ),
),
],
),
) )
: Padding( : Padding(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 12), padding: const EdgeInsets.fromLTRB(12, 4, 12, 12),
@ -411,7 +390,7 @@ class const _RaisedCenterFloat(final double lift)
} }
} }
/// The bibliography, as an outlined round button beside the content. /// The bibliography, as an outlined round button at the foot of the topics.
class const _BibliographyButton({required final Studyset studyset}) class const _BibliographyButton({required final Studyset studyset})
extends StatelessWidget { extends StatelessWidget {
@override @override

View file

@ -19,6 +19,7 @@ import 'package:halkyon_learn_flutter/src/feature/study/widget/study_progress.da
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_breadcrumb.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_breadcrumb.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_rail.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_screen.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_screen.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:material_ui/material_ui.dart'; import 'package:material_ui/material_ui.dart';
@ -382,8 +383,10 @@ void main() {
expect(find.byTooltip('Copy'), findsNWidgets(2)); expect(find.byTooltip('Copy'), findsNWidgets(2));
}); });
testWidgets('the bibliography is a button above the content, not in the ' testWidgets('the bibliography is a button at the foot of the topics, not '
'app bar; on a phone at the foot of the topics', (tester) async { 'in the app bar: in the side bar, and on a phone in the drawer', (
tester,
) async {
stored('japanese', 'Japanese'); stored('japanese', 'Japanese');
await openFromLibrary(tester, 'Japanese'); await openFromLibrary(tester, 'Japanese');
expect( expect(
@ -393,7 +396,13 @@ void main() {
), ),
findsNothing, findsNothing,
); );
expect(find.byTooltip('Bibliography'), findsOneWidget); expect(
find.descendant(
of: find.byType(StudysetRail),
matching: find.byTooltip('Bibliography'),
),
findsOneWidget,
);
await showApp( await showApp(
tester, tester,
@ -412,6 +421,27 @@ void main() {
); );
}); });
testWidgets('in the side bar, the bibliography shares the foot of the '
'topics with New sub-studyset: that at the start, the bibliography at '
'the end', (tester) async {
stored('japanese', 'Japanese');
stored('kana', 'Kana', parent: 'japanese');
await showApp(tester, location: RoutePaths.studyset('japanese'));
await tester.pumpAndSettle();
final rail = tester.getRect(find.byType(StudysetRail));
final create = tester.getRect(
find.ancestor(
of: find.text('New sub-studyset'),
matching: find.byWidgetPredicate((widget) => widget is FilledButton),
),
);
final bibliography = tester.getRect(find.byTooltip('Bibliography'));
expect(bibliography.center.dy, moreOrLessEquals(create.center.dy));
expect(create.left, moreOrLessEquals(rail.left + 16));
expect(bibliography.right, moreOrLessEquals(rail.right - 16));
});
testWidgets('on a phone, the bibliography shares the foot of the topics ' testWidgets('on a phone, the bibliography shares the foot of the topics '
'with New sub-studyset: that at the start, the bibliography at the ' 'with New sub-studyset: that at the start, the bibliography at the '
'end', (tester) async { 'end', (tester) async {