This commit is contained in:
Vadim 2023-08-12 15:16:37 +02:00
parent deb1ac3282
commit 4a88bad074
5 changed files with 170 additions and 4 deletions

13
.vscode/launch.json vendored
View file

@ -8,6 +8,7 @@
"name": "dev (android)",
"request": "launch",
"type": "dart",
"flutterMode": "profile",
"args": [
"--flavor",
"dev",
@ -52,5 +53,17 @@
],
"program": "${workspaceFolder}/lib/main_prod.dart",
},
{
"name": "Integration Test",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"prod",
"--dart-define",
"cameraPreviewAspectRatio=240/320",
],
"program": "${workspaceFolder}/integration_test/widget_dialog_animated_test.dart",
},
],
}

View file

@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:lightmeter/data/models/theme_type.dart';
import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/theme_provider.dart';
import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:mocktail/mocktail.dart';
class _MockUserPreferencesService extends Mock implements UserPreferencesService {}
class ApplicationMock extends StatefulWidget {
final Widget child;
const ApplicationMock({required this.child, super.key});
@override
State<ApplicationMock> createState() => _ApplicationMockState();
}
class _ApplicationMockState extends State<ApplicationMock> {
late final _MockUserPreferencesService userPreferencesService;
@override
void initState() {
super.initState();
userPreferencesService = _MockUserPreferencesService();
when(() => userPreferencesService.themeType).thenReturn(ThemeType.light);
when(() => userPreferencesService.primaryColor)
.thenReturn(ThemeProvider.primaryColorsList.first);
when(() => userPreferencesService.dynamicColor).thenReturn(false);
}
@override
Widget build(BuildContext context) {
return InheritedWidgetBase<UserPreferencesService>(
data: userPreferencesService,
child: ThemeProvider(
child: Builder(
builder: (context) {
return MaterialApp(
theme: context.listen<ThemeData>(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
builder: (context, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: child!,
),
home: widget.child,
);
},
),
),
);
}
}

View file

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:lightmeter/data/models/film.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart';
import 'mocks/application_mock.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('AnimatedDialogPicker test', () {
testWidgets('Tap on `ReadingValueContainer`, verify opened', (tester) async {
await tester.pumpWidget(const ApplicationMock(child: AnimatedPickerTest()));
expect(find.text('Film'), findsOneWidget);
expect(find.text('None'), findsOneWidget);
await tester.tap(find.byType(AnimatedDialogPicker<Film>));
await tester.pumpAndSettle(Dimens.durationL);
expect(find.text('Film'), findsNWidgets(2));
expect(find.text('None'), findsNWidgets(2));
});
});
}
class AnimatedPickerTest extends StatefulWidget {
const AnimatedPickerTest({super.key});
@override
State<AnimatedPickerTest> createState() => _AnimatedPickerTestState();
}
class _AnimatedPickerTestState extends State<AnimatedPickerTest> {
Film _selectedFilm = Film.values.first;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _FilmPicker(
values: Film.values,
selectedValue: _selectedFilm,
onChanged: (value) {
setState(() {
_selectedFilm = value;
});
},
),
),
);
}
}
class _FilmPicker extends StatelessWidget {
final List<Film> values;
final Film selectedValue;
final ValueChanged<Film> onChanged;
const _FilmPicker({
required this.values,
required this.selectedValue,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<Film>(
icon: Icons.camera_roll,
title: "Film",
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? 'None' : value.name),
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: "Film",
value: selectedValue.name.isEmpty ? 'None' : selectedValue.name,
),
),
);
}
}

View file

@ -294,6 +294,7 @@ class _AnimatedSwitcher extends StatelessWidget {
return Stack(
alignment: Alignment.center,
children: [
// https://api.flutter.dev/flutter/widgets/Opacity-class.html#performance-considerations-for-opacity-animation
Opacity(
opacity: closedOpacityAnimation.value,
child: Transform.scale(
@ -304,10 +305,15 @@ class _AnimatedSwitcher extends StatelessWidget {
),
),
),
Opacity(
opacity: openedOpacityAnimation.value,
child: openedChild,
),
/// When dialog is only started expanding there is too little horizontal space,
/// which leads to the failed ListTile assertion (listTileWidget != leading.width).
/// So we show the picker only when it makes sense as it begins to be less opaque.
if (openedOpacityAnimation.value != 0)
Opacity(
opacity: openedOpacityAnimation.value,
child: openedChild,
),
],
);
}

View file

@ -44,6 +44,8 @@ dev_dependencies:
flutter_test:
sdk: flutter
google_fonts: 3.0.1
integration_test:
sdk: flutter
lint: 2.1.2
mocktail: 0.3.0
test: 1.24.1