Skip to content

Commit 30ebf25

Browse files
authored
Development (#113)
2 parents 0125168 + 6845e11 commit 30ebf25

33 files changed

Lines changed: 3203 additions & 75 deletions

.github/workflows/checks.yaml

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,110 @@ jobs:
2525
- name: Install dependencies
2626
run: flutter pub get
2727

28-
- name: Run checks
28+
- name: Run analyze
2929
run: flutter analyze
30+
31+
- name: Run tests
32+
run: flutter test --machine --coverage > test-results.json
33+
34+
- name: Publish test results
35+
uses: dorny/test-reporter@v1
36+
if: always()
37+
with:
38+
name: Unit Tests
39+
path: test-results.json
40+
reporter: flutter-json
41+
42+
- name: Upload coverage
43+
uses: actions/upload-artifact@v4
44+
if: always()
45+
with:
46+
name: flutter-coverage
47+
path: coverage/lcov.info
48+
retention-days: 1
49+
50+
coverage-comment:
51+
runs-on: ubuntu-latest
52+
needs: lint-dart
53+
if: always() && github.event_name == 'pull_request'
54+
55+
steps:
56+
- name: Download coverage
57+
uses: actions/download-artifact@v4
58+
with:
59+
name: flutter-coverage
60+
path: coverage
61+
continue-on-error: true
62+
63+
- name: Install lcov
64+
run: sudo apt-get update && sudo apt-get install -y lcov
65+
66+
- name: Parse coverage
67+
id: coverage
68+
run: |
69+
if [ -f coverage/lcov.info ]; then
70+
# Use lcov to get summary (more reliable than grep)
71+
COVERAGE_SUMMARY=$(lcov --summary coverage/lcov.info 2>&1)
72+
73+
# Extract line coverage percentage
74+
COVERAGE_PCT=$(echo "$COVERAGE_SUMMARY" | grep -oP 'lines\.*: \K[\d.]+(?=%)')
75+
COVERAGE_PCT=${COVERAGE_PCT:-0.0}
76+
77+
# Extract hit/total counts
78+
LINES_HIT=$(grep -o 'DA:' coverage/lcov.info | wc -l)
79+
LINES_TOTAL=$(grep -o 'LF:' coverage/lcov.info | wc -l)
80+
81+
echo "coverage_pct=$COVERAGE_PCT" >> $GITHUB_OUTPUT
82+
echo "lines_hit=$LINES_HIT" >> $GITHUB_OUTPUT
83+
echo "lines_total=$LINES_TOTAL" >> $GITHUB_OUTPUT
84+
else
85+
echo "coverage_pct=0.0" >> $GITHUB_OUTPUT
86+
echo "lines_hit=0" >> $GITHUB_OUTPUT
87+
echo "lines_total=0" >> $GITHUB_OUTPUT
88+
fi
89+
90+
- name: Post coverage comment
91+
uses: actions/github-script@v7
92+
with:
93+
script: |
94+
const coveragePct = '${{ steps.coverage.outputs.coverage_pct }}';
95+
const linesHit = '${{ steps.coverage.outputs.lines_hit }}';
96+
const linesTotal = '${{ steps.coverage.outputs.lines_total }}';
97+
const linesMissing = parseInt(linesTotal) - parseInt(linesHit);
98+
99+
const body = `## 📊 Coverage Report
100+
101+
| Metric | Value |
102+
|--------|-------|
103+
| **Coverage** | **${coveragePct}%** |
104+
| Lines covered | ${linesHit} / ${linesTotal} |
105+
| Lines missing | ${linesMissing} |
106+
107+
<sub>🤖 Coverage report from [checks workflow](${context.payload.repository.html_url}/actions/runs/${context.runId})</sub>`;
108+
109+
// Find existing comment
110+
const { data: comments } = await github.rest.issues.listComments({
111+
owner: context.repo.owner,
112+
repo: context.repo.repo,
113+
issue_number: context.issue.number,
114+
});
115+
116+
const existing = comments.find(c =>
117+
c.user.type === 'Bot' && c.body.includes('## 📊 Coverage Report')
118+
);
119+
120+
if (existing) {
121+
await github.rest.issues.updateComment({
122+
owner: context.repo.owner,
123+
repo: context.repo.repo,
124+
comment_id: existing.id,
125+
body,
126+
});
127+
} else {
128+
await github.rest.issues.createComment({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
issue_number: context.issue.number,
132+
body,
133+
});
134+
}

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,11 @@ credentials.json
4747
# FVM Version Cache
4848
.fvm/
4949

50-
.vscode/settings.json
50+
.vscode/settings.json
51+
52+
ignoreme.dart
53+
54+
# Claude Code
55+
.claude/settings.local.json
56+
57+
coverage/

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 7.5.9
4+
5+
- Added `ThemedPasswordInput` widget with built-in password visibility toggle, strength level indicator, and requirement validation (lowercase, uppercase, digit, special character). Supports `LayrzAppLocalizations` for i18n.
6+
37
## 7.5.8
48

59
- Upgraded `google_fonts` dependency to `^8.0.0` to support latest font features and improvements.

CLAUDE.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
**layrz_theme** is a Flutter widget library (published on pub.dev) that implements the Layrz design standard following Material Design 3 guidelines. It provides inputs, layout systems, tables, theme generators, maps, and more. SDK constraints: Dart >=3.10.0, Flutter >=3.38.1.
8+
9+
## Commands
10+
11+
```bash
12+
flutter pub get # Install dependencies
13+
flutter analyze # Lint (this is what CI runs on PRs)
14+
flutter test # Run all tests
15+
flutter test test/color_test.dart # Run a single test file
16+
```
17+
18+
**Example app:**
19+
```bash
20+
cd example && flutter pub get && flutter run
21+
```
22+
23+
## Architecture
24+
25+
### Module System
26+
27+
The library is organized as a set of sub-libraries under `lib/src/`. Each module directory contains a barrel file (e.g., `inputs.dart`, `layout.dart`) that uses Dart's `library` + `part` directives to compose its implementation files. The top-level `lib/layrz_theme.dart` re-exports all sub-libraries.
28+
29+
**To add a new widget to an existing module:** create the implementation file in the appropriate subdirectory, then add a `part` directive in the module's barrel file.
30+
31+
### Key Modules
32+
33+
| Module | Purpose |
34+
|--------|---------|
35+
| `inputs/` | Form inputs (text, number, select, pickers, code editor, etc.) — largest module (~95 part files) |
36+
| `layout/` | App shell with multiple navigation styles (sidebar, dual bar, mini, bottom nav), breadcrumbs, AppBar |
37+
| `theme/` | Light/dark Material 3 theme generation, color constants, font config via Google Fonts |
38+
| `table2/` | Advanced data table with sorting, filtering, multi-select, infinite scroll, controller pattern |
39+
| `extensions/` | Extension methods on Color, DateTime, Widget, String |
40+
| `helpers/` | Utilities for shadows, avatars, colors, file handling |
41+
| `map/` | Flutter Map integration with toolbar and controls |
42+
| `colorblindness/` | Color blindness simulation filters (6 modes with adjustable strength) |
43+
44+
### Patterns
45+
46+
- **Responsive breakpoints:** `kExtraSmallGrid` (600), `kSmallGrid` (960), `kMediumGrid` (1264), `kLargeGrid` (1904) — used with `LayoutBuilder` throughout.
47+
- **Theme colors:** `kPrimaryColor` (#001e60), `kAccentColor` (#FF8200).
48+
- **State management:** Uses `layrz_state` package. Example app has `AppStore` for theme/colorblind mode.
49+
- **Platform-conditional imports:** File handling uses `if (dart.library.js_interop)` for web vs native.
50+
- **Input pattern:** Widgets accept optional external `FocusNode`/`TextEditingController`; only internally-created controllers are disposed.
51+
- **Enum-based styling:** Components use style enums (e.g., `ThemedButtonStyle`, `ThemedInputStyle`) for variant selection.
52+
53+
## Code Style
54+
55+
- Formatter: 120-char page width, trailing commas preserved (`analysis_options.yaml`)
56+
- Linter: `flutter_lints` package
57+
- Use modern Dart dot shorthand syntax
58+
59+
## CI
60+
61+
- PRs to `main`, `next`, `development` run `flutter analyze` and `flutter test` (Flutter 3.38.8 stable)
62+
- Test results published as check run + PR comment with summary
63+
- Version tags (`v*.*.*`) trigger publish to pub.dev and deploy example to GitHub Pages

Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.PHONY: analyze
2+
analyze:
3+
flutter analyze
4+
5+
.PHONY: test
6+
test:
7+
flutter test
8+
9+
.PHONY: coverage
10+
coverage:
11+
flutter test --coverage
12+
@dart run tools/coverage_report.dart
13+
14+
.PHONY: coverage-summary
15+
coverage-summary:
16+
flutter test --coverage
17+
@dart run tools/coverage_report.dart --summary

example/lib/router.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ final goRoutes = [
145145
];
146146

147147
final router = GoRouter(
148-
initialLocation: kDebugMode ? '/inputs/chips' : '/',
148+
initialLocation: kDebugMode ? '/inputs/text' : '/',
149149
errorPageBuilder: (context, state) => customTransitionBuilder(context, state, const NotFoundView()),
150150
routes: goRoutes,
151151
);

example/lib/views/inputs/src/text.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class TextInputView extends StatefulWidget {
1010
class _TextInputViewState extends State<TextInputView> {
1111
num? _value;
1212
String? _text;
13+
String _password = "Abc123!@#";
1314
Duration? _dur = const Duration();
1415
@override
1516
Widget build(BuildContext context) {
@@ -187,6 +188,22 @@ class _TextInputViewState extends State<TextInputView> {
187188
value: _dur,
188189
onChanged: (value) => setState(() => _dur = value),
189190
),
191+
const SizedBox(height: 10),
192+
Text(
193+
"Finally, you can also use ThemedPasswordInput to handle passwords easly, like the following example:",
194+
style: Theme.of(context).textTheme.titleMedium?.copyWith(
195+
fontWeight: FontWeight.bold,
196+
),
197+
),
198+
const SizedBox(height: 10),
199+
ThemedPasswordInput(
200+
labelText: "Password field",
201+
value: _password,
202+
onChanged: (value) {
203+
debugPrint("Value: $value");
204+
setState(() => _password = value);
205+
},
206+
),
190207
],
191208
),
192209
),

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ packages:
374374
path: ".."
375375
relative: true
376376
source: path
377-
version: "7.5.8"
377+
version: "7.5.9"
378378
leak_tracker:
379379
dependency: transitive
380380
description:

ignoreme.dart

Lines changed: 0 additions & 47 deletions
This file was deleted.

lib/src/inputs/inputs.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ part 'src/general/radio_input.dart';
5454
part 'src/general/search_input.dart';
5555
part 'src/general/select_input.dart';
5656
part 'src/general/multiselect_input.dart';
57+
part 'src/general/password_input.dart';
5758

5859
// Not-reviewed inputs (Basically not documented and/or upgraded to new format)
5960
part 'src/code_editor.dart';

0 commit comments

Comments
 (0)