todo-app/lib/ui/notes_grid.dart
Krzysztof kuhy Rudnicki 67212cd7ec
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
One list: repo groups, repo search, compact filter
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 13:40:20 +01:00

88 lines
2.4 KiB
Dart

import 'dart:math' as math;
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:todo/data/note.dart';
import 'package:todo/ui/note_grid_tile.dart';
import 'package:todo/ui/repo_group.dart';
import 'package:todo/ui/repo_tiles.dart';
/// Below this width the notes list is a grid; at or above it, the list.
const double kNotesGridMaxWidth = 600;
const _columns = 2;
const double _gap = AppSpacing.sm;
const double _sidePadding = AppSpacing.md - AppSpacing.xs;
const _minTileHeight = 96.0;
double _tileHeightFor(double height) {
final rows = math.max(1, ((height + _gap) / (_minTileHeight + _gap)).floor());
return (height + _gap) / rows - _gap;
}
/// Phone notes list: two columns, scrolling only past one screenful.
class NotesGrid extends StatelessWidget {
/// Creates the grid; [onOpen] and [onActions] get the tapped note.
const NotesGrid({
required this.entries,
required this.onOpen,
required this.onActions,
required this.onOpenRepo,
super.key,
});
/// Rows in display order: single notes and repo groups.
final List<ListEntry> entries;
/// Tap on a tile.
final ValueChanged<Note> onOpen;
/// Long-press on a tile.
final ValueChanged<Note> onActions;
/// Tap on a repo group tile.
final ValueChanged<RepoEntry> onOpenRepo;
Widget _tile(ListEntry entry) => switch (entry) {
NoteEntry(:final note) => NoteGridTile(
key: ValueKey(note.id),
note: note,
onTap: () => onOpen(note),
onActions: () => onActions(note),
),
final RepoEntry repo => RepoGridTile(
key: ValueKey(repo.repo),
entry: repo,
onTap: () => onOpenRepo(repo),
),
};
SliverGridDelegate _delegate(double viewportHeight) {
return SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _columns,
mainAxisSpacing: _gap,
crossAxisSpacing: _gap,
mainAxisExtent: _tileHeightFor(viewportHeight),
);
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: _gap),
child: SafeArea(
top: false,
child: LayoutBuilder(
builder: (context, box) => GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: _sidePadding),
gridDelegate: _delegate(box.maxHeight),
itemCount: entries.length,
itemBuilder: (context, i) => _tile(entries[i]),
),
),
),
);
}
}