WIP
This commit is contained in:
commit
005e24c1f5
|
@ -12,8 +12,7 @@ import 'package:hub/features/local/index.dart';
|
||||||
import 'package:hub/features/menu/index.dart';
|
import 'package:hub/features/menu/index.dart';
|
||||||
import 'package:hub/features/module/data/index.dart';
|
import 'package:hub/features/module/data/index.dart';
|
||||||
import 'package:hub/features/module/domain/index.dart';
|
import 'package:hub/features/module/domain/index.dart';
|
||||||
import 'package:hub/features/storage/enums/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
import 'package:hub/features/storage/repositories/storage_repository_impl.dart';
|
|
||||||
import 'package:hub/flutter_flow/index.dart' as ff;
|
import 'package:hub/flutter_flow/index.dart' as ff;
|
||||||
import 'package:hub/initialization.dart';
|
import 'package:hub/initialization.dart';
|
||||||
import 'package:hub/main.dart';
|
import 'package:hub/main.dart';
|
||||||
|
@ -31,10 +30,7 @@ part 'profile_test.dart';
|
||||||
part 'property_test.dart';
|
part 'property_test.dart';
|
||||||
part 'setting_test.dart';
|
part 'setting_test.dart';
|
||||||
part 'storage_test.dart';
|
part 'storage_test.dart';
|
||||||
part 'utils_test.dart';rage_test.dart';
|
|
||||||
|
|
||||||
part 'utils_test.dart';
|
part 'utils_test.dart';
|
||||||
|
|
||||||
part 'welcome_test.dart';
|
part 'welcome_test.dart';
|
||||||
|
|
||||||
late PatrolTester $;
|
late PatrolTester $;
|
||||||
|
@ -43,20 +39,29 @@ void main() {
|
||||||
//init integration test
|
//init integration test
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
WelcomeTest.signInToSignUp();
|
setUp(() {});
|
||||||
WelcomeTest.signUpToSignIn();
|
tearDown(() async {
|
||||||
|
// await StorageHelper().clean(Storage.databaseStorage);
|
||||||
|
// await StorageHelper().clean(Storage.secureStorage);
|
||||||
|
//
|
||||||
|
// DatabaseService.isInitialized = false;
|
||||||
|
// await DatabaseService.instance.init();
|
||||||
|
});
|
||||||
|
|
||||||
AuthenticationTest.signIn();
|
// WelcomeTest.signInToSignUp();
|
||||||
AuthenticationTest.signUp();
|
// WelcomeTest.signUpToSignIn();
|
||||||
AuthenticationTest.signOut();
|
//
|
||||||
|
// AuthenticationTest.signIn();
|
||||||
|
// AuthenticationTest.signUp();
|
||||||
|
// AuthenticationTest.signOut();
|
||||||
|
//
|
||||||
|
// ModularizationTest.switchLicense();
|
||||||
|
// ModularizationTest.containLicense();
|
||||||
|
|
||||||
ModularizationTest.switchLicense();
|
// MenuTest.navToEntries();
|
||||||
ModularizationTest.containLicense();
|
// MenuTest.containEntries();
|
||||||
|
|
||||||
MenuTest.navToEntries();
|
|
||||||
MenuTest.containEntries();
|
|
||||||
MenuTest.labels2AppbarConsistency();
|
MenuTest.labels2AppbarConsistency();
|
||||||
|
|
||||||
LocalsTest.setLocal();
|
// LocalsTest.setLocal();
|
||||||
LocalsTest.unlinkLocal();
|
// LocalsTest.unlinkLocal();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,10 @@ class LocalsTest {
|
||||||
expect(unlinkButton, findsOneWidget);
|
expect(unlinkButton, findsOneWidget);
|
||||||
await $(unlinkButton).tap();
|
await $(unlinkButton).tap();
|
||||||
|
|
||||||
await $('Sim') //
|
final PatrolFinder alertDialog = await $(#AlertDialogKey) //
|
||||||
|
.waitUntilVisible();
|
||||||
|
await alertDialog
|
||||||
|
.$(#AcceptOptionKey) //
|
||||||
.waitUntilVisible()
|
.waitUntilVisible()
|
||||||
.tap(settlePolicy: SettlePolicy.noSettle);
|
.tap(settlePolicy: SettlePolicy.noSettle);
|
||||||
|
|
||||||
|
@ -139,7 +142,12 @@ class LocalsTest {
|
||||||
if (entriesFinder.evaluate().isNotEmpty) {
|
if (entriesFinder.evaluate().isNotEmpty) {
|
||||||
await $(entriesFinder.first).waitUntilVisible().tap();
|
await $(entriesFinder.first).waitUntilVisible().tap();
|
||||||
|
|
||||||
await $(#AcceptOptionKey).waitUntilVisible().tap();
|
final PatrolFinder alertDialog = await $(#AlertDialogKey) //
|
||||||
|
.waitUntilVisible();
|
||||||
|
await alertDialog
|
||||||
|
.$(#AcceptOptionKey) //
|
||||||
|
.waitUntilVisible()
|
||||||
|
.tap(settlePolicy: SettlePolicy.noSettle);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
|
|
@ -25,11 +25,12 @@ class MenuTest {
|
||||||
|
|
||||||
final LinkedHashMap<String, String> routesTitles =
|
final LinkedHashMap<String, String> routesTitles =
|
||||||
LinkedHashMap.fromIterables(routes, titles);
|
LinkedHashMap.fromIterables(routes, titles);
|
||||||
|
late String route;
|
||||||
|
late String title;
|
||||||
|
|
||||||
for (final entry in routesTitles.entries) {
|
for (final entry in routesTitles.entries) {
|
||||||
await $.pumpAndSettle();
|
route = entry.key;
|
||||||
final String route = entry.key;
|
title = entry.value;
|
||||||
final String title = entry.value;
|
|
||||||
|
|
||||||
print('route: $route');
|
print('route: $route');
|
||||||
print('title: $title');
|
print('title: $title');
|
||||||
|
@ -38,14 +39,15 @@ class MenuTest {
|
||||||
if (route == '/fastPassPage') continue;
|
if (route == '/fastPassPage') continue;
|
||||||
if (route == '/reservation') continue;
|
if (route == '/reservation') continue;
|
||||||
|
|
||||||
await $.pumpAndSettle();
|
|
||||||
ff.navigatorKey.currentContext!.go(route);
|
ff.navigatorKey.currentContext!.go(route);
|
||||||
await $.pumpAndSettle();
|
|
||||||
|
|
||||||
Future.delayed(const Duration(milliseconds: 500));
|
Future.delayed(const Duration(milliseconds: 500));
|
||||||
await $.waitUntilExists($(AppBar));
|
final PatrolFinder appBar = await $(AppBar) //
|
||||||
final PatrolFinder appBar = $(title);
|
.waitUntilExists();
|
||||||
expect(appBar, findsOneWidget);
|
final PatrolFinder titleAppBar = await appBar //
|
||||||
|
.$(title)
|
||||||
|
.waitUntilVisible();
|
||||||
|
expect(titleAppBar, findsOneWidget);
|
||||||
}
|
}
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hub/features/backend/index.dart';
|
import 'package:hub/features/backend/index.dart';
|
||||||
import 'package:hub/features/local/index.dart';
|
import 'package:hub/features/local/index.dart';
|
||||||
import 'package:hub/features/module/data/repositories/license_repository_impl.dart';
|
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
|
|
||||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||||
import 'package:hub/shared/utils/device_util.dart';
|
import 'package:hub/shared/utils/device_util.dart';
|
||||||
import 'package:hub/shared/utils/dialog_util.dart';
|
import 'package:hub/shared/utils/dialog_util.dart';
|
||||||
import 'package:hub/shared/utils/log_util.dart';
|
import 'package:hub/shared/utils/log_util.dart';
|
||||||
|
|
||||||
import 'package:hub/shared/utils/snackbar_util.dart';
|
import 'package:hub/shared/utils/snackbar_util.dart';
|
||||||
|
|
||||||
import '../../../flutter_flow/flutter_flow_util.dart';
|
import '../../../flutter_flow/flutter_flow_util.dart';
|
||||||
|
@ -151,7 +148,7 @@ class AuthenticationService {
|
||||||
|
|
||||||
await StorageHelper().clean(Storage.databaseStorage);
|
await StorageHelper().clean(Storage.databaseStorage);
|
||||||
await StorageHelper().clean(Storage.secureStorage);
|
await StorageHelper().clean(Storage.secureStorage);
|
||||||
await LicenseRepositoryImpl().cleanLicense();
|
|
||||||
DatabaseService.isInitialized = false;
|
DatabaseService.isInitialized = false;
|
||||||
await DatabaseService.instance.init();
|
await DatabaseService.instance.init();
|
||||||
|
|
||||||
|
|
|
@ -159,8 +159,8 @@ class ProvisionalHistoryState extends State<ProvisionalHistoryPage> {
|
||||||
{
|
{
|
||||||
'title': FFLocalizations.of(context)
|
'title': FFLocalizations.of(context)
|
||||||
.getVariableText(
|
.getVariableText(
|
||||||
ptText: 'Convidado',
|
ptText: 'Concluído',
|
||||||
enText: 'Guest',
|
enText: 'Completed',
|
||||||
),
|
),
|
||||||
'value': 'CO',
|
'value': 'CO',
|
||||||
},
|
},
|
||||||
|
|
|
@ -37,7 +37,7 @@ class MenuEntry implements BaseModule {
|
||||||
icon: Icons.sports_motorsports_outlined,
|
icon: Icons.sports_motorsports_outlined,
|
||||||
name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(
|
name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(
|
||||||
ptText: 'Agendar Entregas',
|
ptText: 'Agendar Entregas',
|
||||||
enText: 'Schedule Delivery',
|
enText: 'Delivery Schedule',
|
||||||
),
|
),
|
||||||
route: '/deliverySchedule',
|
route: '/deliverySchedule',
|
||||||
types: [MenuEntryType.Home, MenuEntryType.Drawer],
|
types: [MenuEntryType.Home, MenuEntryType.Drawer],
|
||||||
|
|
|
@ -9,9 +9,7 @@ import 'package:hub/features/module/index.dart';
|
||||||
|
|
||||||
abstract class LicenseRemoteDataSource {
|
abstract class LicenseRemoteDataSource {
|
||||||
Future<bool> fetchLicenses(bool isNewVersion);
|
Future<bool> fetchLicenses(bool isNewVersion);
|
||||||
|
|
||||||
Future<bool> cleanLicense();
|
Future<bool> cleanLicense();
|
||||||
|
|
||||||
Future<void> processLicense();
|
Future<void> processLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:hub/features/backend/index.dart';
|
|
||||||
import 'package:hub/features/module/data/data_sources/index.dart';
|
import 'package:hub/features/module/data/data_sources/index.dart';
|
||||||
import 'package:hub/features/module/domain/index.dart';
|
import 'package:hub/features/module/domain/index.dart';
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
import '../../../backend/index.dart';
|
||||||
|
|
||||||
typedef LicenseStatus = bool;
|
typedef LicenseStatus = bool;
|
||||||
|
|
||||||
class LicenseRepositoryImpl implements LicenseRepository {
|
class LicenseRepositoryImpl implements LicenseRepository {
|
||||||
|
@ -21,14 +22,6 @@ class LicenseRepositoryImpl implements LicenseRepository {
|
||||||
remoteDataSource = LicenseRemoteDataSourceImpl(this.api);
|
remoteDataSource = LicenseRemoteDataSourceImpl(this.api);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> resetLicense() async {
|
|
||||||
bool result = false;
|
|
||||||
final bool isNewVersion = await localDataSource.isNewVersion();
|
|
||||||
result = await localDataSource.setupLicense(isNewVersion);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> updateLicense() async {
|
Future<bool> updateLicense() async {
|
||||||
log('updateLicense');
|
log('updateLicense');
|
||||||
|
@ -36,6 +29,7 @@ class LicenseRepositoryImpl implements LicenseRepository {
|
||||||
final bool isNewVersion = await localDataSource.isNewVersion();
|
final bool isNewVersion = await localDataSource.isNewVersion();
|
||||||
|
|
||||||
if (isNewVersion) {
|
if (isNewVersion) {
|
||||||
|
await localDataSource.setupLicense(isNewVersion);
|
||||||
result = await remoteDataSource.fetchLicenses(isNewVersion);
|
result = await remoteDataSource.fetchLicenses(isNewVersion);
|
||||||
} else {
|
} else {
|
||||||
result = await localDataSource.setupLicense(isNewVersion);
|
result = await localDataSource.setupLicense(isNewVersion);
|
||||||
|
@ -64,4 +58,12 @@ class LicenseRepositoryImpl implements LicenseRepository {
|
||||||
Future<List<String>> getLicense() async {
|
Future<List<String>> getLicense() async {
|
||||||
return await localDataSource.getLicense();
|
return await localDataSource.getLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> resetLicense() async {
|
||||||
|
bool result = false;
|
||||||
|
final bool isNewVersion = await localDataSource.isNewVersion();
|
||||||
|
result = await localDataSource.setupLicense(isNewVersion);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:hub/features/storage/index.dart';
|
||||||
|
|
||||||
class DatabaseStorage implements BaseStorage {
|
class DatabaseStorage implements BaseStorage {
|
||||||
DatabaseStorage._();
|
DatabaseStorage._();
|
||||||
|
|
||||||
static final DatabaseStorage instance = DatabaseStorage._();
|
static final DatabaseStorage instance = DatabaseStorage._();
|
||||||
|
|
||||||
// static final DatabaseStorage instance = DatabaseStorage._();
|
// static final DatabaseStorage instance = DatabaseStorage._();
|
||||||
|
@ -30,12 +31,15 @@ class DatabaseStorage implements BaseStorage {
|
||||||
@override
|
@override
|
||||||
Future<void> delete(String key) async =>
|
Future<void> delete(String key) async =>
|
||||||
await getInstanceByKey(key).delete(key);
|
await getInstanceByKey(key).delete(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String?> get(String key) async => //
|
Future<String?> get(String key) async => //
|
||||||
await getInstanceByKey(key).get(key);
|
await getInstanceByKey(key).get(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async => //
|
Future<void> init() async => //
|
||||||
await DatabaseService.instance.init();
|
await DatabaseService.instance.init();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> set<T>(String key, T value) async =>
|
Future<void> set<T>(String key, T value) async =>
|
||||||
await getInstanceByKey(key).set(key, value);
|
await getInstanceByKey(key).set(key, value);
|
||||||
|
|
|
@ -15,20 +15,25 @@ class FFLocalizations {
|
||||||
static List<String> languages() => ['pt', 'en'];
|
static List<String> languages() => ['pt', 'en'];
|
||||||
|
|
||||||
static late SharedPreferences _prefs;
|
static late SharedPreferences _prefs;
|
||||||
|
|
||||||
static Future initialize() async =>
|
static Future initialize() async =>
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
static Future storeLocale(String locale) =>
|
static Future storeLocale(String locale) =>
|
||||||
_prefs.setString(_kLocaleStorageKey, locale);
|
_prefs.setString(_kLocaleStorageKey, locale);
|
||||||
|
|
||||||
static Locale? getStoredLocale() {
|
static Locale? getStoredLocale() {
|
||||||
final locale = _prefs.getString(_kLocaleStorageKey);
|
final locale = _prefs.getString(_kLocaleStorageKey);
|
||||||
return locale != null && locale.isNotEmpty ? createLocale(locale) : null;
|
return locale != null && locale.isNotEmpty ? createLocale(locale) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get languageCode => locale.toString();
|
String get languageCode => locale.toString();
|
||||||
|
|
||||||
String? get languageShortCode =>
|
String? get languageShortCode =>
|
||||||
_languagesWithShortCode.contains(locale.toString())
|
_languagesWithShortCode.contains(locale.toString())
|
||||||
? '${locale.toString()}_short'
|
? '${locale.toString()}_short'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
int get languageIndex => languages().contains(languageCode)
|
int get languageIndex => languages().contains(languageCode)
|
||||||
? languages().indexOf(languageCode)
|
? languages().indexOf(languageCode)
|
||||||
: 0;
|
: 0;
|
||||||
|
@ -443,7 +448,7 @@ final kTranslationsMap = <Map<String, Map<String, String>>>[
|
||||||
},
|
},
|
||||||
'784f83pc': {
|
'784f83pc': {
|
||||||
'pt': 'Consultar Liberações',
|
'pt': 'Consultar Liberações',
|
||||||
'en': 'Liberation History',
|
'en': 'Liberations History',
|
||||||
},
|
},
|
||||||
'1skj43ye': {
|
'1skj43ye': {
|
||||||
'pt': 'Home',
|
'pt': 'Home',
|
||||||
|
|
|
@ -8,15 +8,14 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:hub/features/notification/index.dart';
|
import 'package:hub/features/notification/index.dart';
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/internationalization.dart';
|
import 'package:hub/flutter_flow/internationalization.dart';
|
||||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||||
import 'package:hub/shared/utils/test_util.dart';
|
import 'package:hub/shared/utils/test_util.dart';
|
||||||
|
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
import 'initialization.dart';
|
import 'initialization.dart';
|
||||||
|
|
||||||
|
@ -176,7 +175,9 @@ class _AppState extends State<App> {
|
||||||
_router = createRouter(_appStateNotifier);
|
_router = createRouter(_appStateNotifier);
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
const Duration(milliseconds: 1000),
|
const Duration(milliseconds: 1000),
|
||||||
() => setState(() => _appStateNotifier.stopShowingSplashImage()),
|
() => mounted
|
||||||
|
? setState(() => _appStateNotifier.stopShowingSplashImage())
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
_setupFirebaseMessaging();
|
_setupFirebaseMessaging();
|
||||||
|
|
|
@ -136,7 +136,7 @@ class _VisitsOnThePropertyState extends State<VisitsOnTheProperty>
|
||||||
title: Text(
|
title: Text(
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
ptText: 'Visitas Abertas',
|
ptText: 'Visitas Abertas',
|
||||||
enText: 'Opened visits',
|
enText: 'Opened Visits',
|
||||||
),
|
),
|
||||||
style: FlutterFlowTheme.of(context).headlineMedium.override(
|
style: FlutterFlowTheme.of(context).headlineMedium.override(
|
||||||
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
|
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Flutter code sample for [PageView].
|
||||||
|
|
||||||
|
void main() => runApp(const PageViewExampleApp());
|
||||||
|
|
||||||
|
class PageViewExampleApp extends StatelessWidget {
|
||||||
|
const PageViewExampleApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('PageView Sample')),
|
||||||
|
body: const PageViewExample(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageViewExample extends StatefulWidget {
|
||||||
|
const PageViewExample({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PageViewExample> createState() => _PageViewExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PageViewExampleState extends State<PageViewExample>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
late PageController _pageViewController;
|
||||||
|
late TabController _tabController;
|
||||||
|
int _currentPageIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pageViewController = PageController();
|
||||||
|
_tabController = TabController(length: 3, vsync: this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_pageViewController.dispose();
|
||||||
|
_tabController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final TextTheme textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
children: <Widget>[
|
||||||
|
PageView(
|
||||||
|
/// [PageView.scrollDirection] defaults to [Axis.horizontal].
|
||||||
|
/// Use [Axis.vertical] to scroll vertically.
|
||||||
|
controller: _pageViewController,
|
||||||
|
onPageChanged: _handlePageViewChanged,
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: Text('First Page', style: textTheme.titleLarge)),
|
||||||
|
Center(child: Text('Second Page', style: textTheme.titleLarge)),
|
||||||
|
Center(child: Text('Third Page', style: textTheme.titleLarge)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
PageIndicator(
|
||||||
|
tabController: _tabController,
|
||||||
|
currentPageIndex: _currentPageIndex,
|
||||||
|
onUpdateCurrentPageIndex: _updateCurrentPageIndex,
|
||||||
|
isOnDesktopAndWeb: _isOnDesktopAndWeb,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handlePageViewChanged(int currentPageIndex) {
|
||||||
|
if (!_isOnDesktopAndWeb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_tabController.index = currentPageIndex;
|
||||||
|
setState(() {
|
||||||
|
_currentPageIndex = currentPageIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateCurrentPageIndex(int index) {
|
||||||
|
_tabController.index = index;
|
||||||
|
_pageViewController.animateToPage(
|
||||||
|
index,
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _isOnDesktopAndWeb =>
|
||||||
|
kIsWeb ||
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
TargetPlatform.macOS ||
|
||||||
|
TargetPlatform.linux ||
|
||||||
|
TargetPlatform.windows =>
|
||||||
|
true,
|
||||||
|
TargetPlatform.android ||
|
||||||
|
TargetPlatform.iOS ||
|
||||||
|
TargetPlatform.fuchsia =>
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Page indicator for desktop and web platforms.
|
||||||
|
///
|
||||||
|
/// On Desktop and Web, drag gesture for horizontal scrolling in a PageView is disabled by default.
|
||||||
|
/// You can defined a custom scroll behavior to activate drag gestures,
|
||||||
|
/// see https://docs.flutter.dev/release/breaking-changes/default-scroll-behavior-drag.
|
||||||
|
///
|
||||||
|
/// In this sample, we use a TabPageSelector to navigate between pages,
|
||||||
|
/// in order to build natural behavior similar to other desktop applications.
|
||||||
|
class PageIndicator extends StatelessWidget {
|
||||||
|
const PageIndicator({
|
||||||
|
super.key,
|
||||||
|
required this.tabController,
|
||||||
|
required this.currentPageIndex,
|
||||||
|
required this.onUpdateCurrentPageIndex,
|
||||||
|
required this.isOnDesktopAndWeb,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int currentPageIndex;
|
||||||
|
final TabController tabController;
|
||||||
|
final void Function(int) onUpdateCurrentPageIndex;
|
||||||
|
final bool isOnDesktopAndWeb;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (!isOnDesktopAndWeb) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
splashRadius: 16.0,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
onPressed: () {
|
||||||
|
if (currentPageIndex == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onUpdateCurrentPageIndex(currentPageIndex - 1);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_left_rounded, size: 32.0),
|
||||||
|
),
|
||||||
|
TabPageSelector(
|
||||||
|
controller: tabController,
|
||||||
|
color: colorScheme.surface,
|
||||||
|
selectedColor: colorScheme.primary,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
splashRadius: 16.0,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
onPressed: () {
|
||||||
|
if (currentPageIndex == 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onUpdateCurrentPageIndex(currentPageIndex + 1);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_right_rounded, size: 32.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ description: A new Flutter project.
|
||||||
|
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.3.0+19
|
version: 1.3.2+21
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|
Loading…
Reference in New Issue