Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit f55e51a

Browse files
Jan-Stepienfelangel
Jan-Stepien
andauthored
feat: capture view implementation (#29)
* Add animations to capture view * button styling changes * fix unit tests * split apptheme to seperate file * implicit scaffold's background theme * use project barrel file * barrel coverage fix * code review fixes * implement PR's comments * theme fix * top level theme * max score in cubit state * move business logic to cubit * add i18n * refactor(capture_page): cubit usage adjustments (#52) * fix tests * localize chip names * fix cubit tests * change row trick into align * medium typo fix * removed * supportedLocales fix * missing trailing coma Co-authored-by: Felix Angelov <[email protected]>
1 parent 1912614 commit f55e51a

23 files changed

+850
-23
lines changed

.vscode/launch.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "flutter_nps",
9+
"cwd": "flutter_nps",
10+
"request": "launch",
11+
"type": "dart"
12+
},
13+
{
14+
"name": "flutter_nps (profile mode)",
15+
"cwd": "flutter_nps",
16+
"request": "launch",
17+
"type": "dart",
18+
"flutterMode": "profile"
19+
}
20+
]
21+
}

flutter_nps/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,109 @@ $ genhtml coverage/lcov.info -o coverage/
3232
$ open coverage/index.html
3333
```
3434

35+
## Working with Translations 🌐
36+
37+
This project relies on [flutter_localizations][flutter_localizations_link] and follows the [official internationalization guide for Flutter][internationalization_link].
38+
39+
### Adding Strings
40+
41+
1. To add a new localizable string, open the `app_en.arb` file at `lib/l10n/arb/app_en.arb`.
42+
43+
```arb
44+
{
45+
"@@locale": "en",
46+
"counterAppBarTitle": "Counter",
47+
"@counterAppBarTitle": {
48+
"description": "Text shown in the AppBar of the Counter Page"
49+
}
50+
}
51+
```
52+
53+
2. Then add a new key/value and description
54+
55+
```arb
56+
{
57+
"@@locale": "en",
58+
"counterAppBarTitle": "Counter",
59+
"@counterAppBarTitle": {
60+
"description": "Text shown in the AppBar of the Counter Page"
61+
},
62+
"helloWorld": "Hello World",
63+
"@helloWorld": {
64+
"description": "Hello World Text"
65+
}
66+
}
67+
```
68+
69+
3. Use the new string
70+
71+
```dart
72+
import 'package:very_good_project/l10n/l10n.dart';
73+
74+
@override
75+
Widget build(BuildContext context) {
76+
final l10n = context.l10n;
77+
return Text(l10n.helloWorld);
78+
}
79+
```
80+
81+
### Adding Supported Locales
82+
83+
Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info.plist` to include the new locale.
84+
85+
```xml
86+
...
87+
88+
<key>CFBundleLocalizations</key>
89+
<array>
90+
<string>en</string>
91+
<string>es</string>
92+
</array>
93+
94+
...
95+
```
96+
97+
### Adding Translations
98+
99+
1. For each supported locale, add a new ARB file in `lib/l10n/arb`.
100+
101+
```
102+
├── l10n
103+
│ ├── arb
104+
│ │ ├── app_en.arb
105+
│ │ └── app_es.arb
106+
```
107+
108+
2. Add the translated strings to each `.arb` file:
109+
110+
`app_en.arb`
111+
112+
```arb
113+
{
114+
"@@locale": "en",
115+
"counterAppBarTitle": "Counter",
116+
"@counterAppBarTitle": {
117+
"description": "Text shown in the AppBar of the Counter Page"
118+
}
119+
}
120+
```
121+
122+
`app_es.arb`
123+
124+
```arb
125+
{
126+
"@@locale": "es",
127+
"counterAppBarTitle": "Contador",
128+
"@counterAppBarTitle": {
129+
"description": "Texto mostrado en la AppBar de la página del contador"
130+
}
131+
}
132+
```
133+
134+
35135
[coverage_badge]: coverage_badge.svg
136+
[flutter_localizations_link]: https://api.flutter.dev/flutter/flutter_localizations/flutter_localizations-library.html
137+
[internationalization_link]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
36138
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
37139
[license_link]: https://opensource.org/licenses/MIT
38140
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg

flutter_nps/l10n.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
arb-dir: lib/l10n/arb
2+
template-arb-file: app_en.arb
3+
output-localization-file: app_localizations.dart
4+
nullable-getter: false

flutter_nps/lib/app/view/app.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,27 @@
66
// https://opensource.org/licenses/MIT.
77

88
import 'package:flutter/material.dart';
9-
import 'package:flutter_nps/colors.dart';
9+
import 'package:flutter_localizations/flutter_localizations.dart';
10+
import 'package:flutter_nps/flutter_nps.dart';
11+
import 'package:flutter_nps/l10n/l10n.dart';
1012

1113
class App extends StatelessWidget {
1214
const App({Key? key}) : super(key: key);
1315

1416
@override
1517
Widget build(BuildContext context) {
1618
return MaterialApp(
17-
theme: ThemeData(
18-
colorScheme: ColorScheme.fromSwatch(
19-
accentColor: NpsColors.colorSecondary,
20-
),
21-
),
19+
theme: AppTheme().theme,
2220
home: const Scaffold(
23-
backgroundColor: Colors.transparent,
24-
body: Text('Sample project'),
21+
body: CapturePage(),
2522
),
23+
localizationsDelegates: const [
24+
AppLocalizations.delegate,
25+
GlobalMaterialLocalizations.delegate,
26+
GlobalWidgetsLocalizations.delegate,
27+
GlobalCupertinoLocalizations.delegate,
28+
],
29+
supportedLocales: AppLocalizations.supportedLocales,
2630
);
2731
}
2832
}

flutter_nps/lib/app_theme.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_nps/flutter_nps.dart';
3+
4+
class AppTheme {
5+
ThemeData get theme => ThemeData(
6+
colorScheme: ColorScheme.fromSwatch(
7+
accentColor: NpsColors.colorSecondary,
8+
backgroundColor: NpsColors.colorWhite,
9+
),
10+
scaffoldBackgroundColor: NpsColors.colorWhite,
11+
elevatedButtonTheme: ElevatedButtonThemeData(
12+
style: ElevatedButton.styleFrom(
13+
primary: NpsColors.colorSecondary,
14+
shape: RoundedRectangleBorder(
15+
borderRadius: BorderRadius.circular(24),
16+
),
17+
).copyWith(
18+
backgroundColor: MaterialStateProperty.resolveWith<Color?>(
19+
(Set<MaterialState> states) {
20+
if (!states.contains(MaterialState.disabled)) {
21+
return NpsColors.colorSecondary;
22+
} else if (states.contains(MaterialState.disabled)) {
23+
return NpsColors.colorWhite;
24+
}
25+
return null;
26+
},
27+
),
28+
),
29+
),
30+
textTheme: const TextTheme(
31+
headline5: NpsStyles.headline5,
32+
subtitle1: NpsStyles.subtitle1,
33+
bodyText2: NpsStyles.link,
34+
),
35+
);
36+
}

flutter_nps/lib/capture/capture.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) 2021, Very Good Ventures
2+
// https://verygood.ventures
3+
//
4+
// Use of this source code is governed by an MIT-style
5+
// license that can be found in the LICENSE file or at
6+
// https://opensource.org/licenses/MIT.
7+
8+
export 'cubit/capture_cubit.dart';
9+
export 'cubit/capture_cubit_state.dart';
10+
export 'view/capture_page.dart';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2021, Very Good Ventures
2+
// https://verygood.ventures
3+
//
4+
// Use of this source code is governed by an MIT-style
5+
// license that can be found in the LICENSE file or at
6+
// https://opensource.org/licenses/MIT.
7+
8+
import 'package:bloc/bloc.dart';
9+
import 'package:flutter_nps/flutter_nps.dart';
10+
11+
class CaptureCubit extends Cubit<CaptureCubitState> {
12+
CaptureCubit() : super(CaptureCubitState.initial());
13+
14+
void selectScore({required int score}) => emit(state.copyWith(score: score));
15+
16+
void chipToggled({required int index}) => state.chipIndexes.contains(index)
17+
? _removeChipIndex(index: index)
18+
: _addChipIndex(index: index);
19+
20+
void _addChipIndex({required int index}) =>
21+
emit(state.copyWith(chipIndexes: [...state.chipIndexes, index]));
22+
23+
void _removeChipIndex({required int index}) => emit(
24+
state.copyWith(
25+
chipIndexes: [...state.chipIndexes.where((item) => item != index)],
26+
),
27+
);
28+
29+
void submitResult() {}
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:equatable/equatable.dart';
2+
3+
class CaptureCubitState extends Equatable {
4+
const CaptureCubitState({
5+
required this.score,
6+
required this.chipIndexes,
7+
this.maxScore = 5,
8+
});
9+
10+
factory CaptureCubitState.initial() =>
11+
const CaptureCubitState(score: -1, chipIndexes: []);
12+
13+
final int score;
14+
final List<int> chipIndexes;
15+
final int maxScore;
16+
17+
CaptureCubitState copyWith({int? score, List<int>? chipIndexes}) =>
18+
CaptureCubitState(
19+
score: score ?? this.score,
20+
chipIndexes: chipIndexes ?? this.chipIndexes,
21+
maxScore: maxScore,
22+
);
23+
24+
@override
25+
List<Object> get props => [score, chipIndexes, maxScore];
26+
}

0 commit comments

Comments
 (0)