diff --git a/lib/data/models/task_model.dart b/lib/data/models/task_model.dart index 5e90563f3..9e3d687a0 100644 --- a/lib/data/models/task_model.dart +++ b/lib/data/models/task_model.dart @@ -190,6 +190,7 @@ abstract class TaskEntity extends Object TaskEntity get clone => rebuild((b) => b ..id = BaseEntity.nextId + ..number = '' ..isChanged = false ..isDeleted = false ..invoiceId = null diff --git a/lib/main_app.dart b/lib/main_app.dart index 3f6bab380..f6ca7e0ed 100644 --- a/lib/main_app.dart +++ b/lib/main_app.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:intl/intl.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; @@ -173,221 +174,242 @@ class InvoiceNinjaAppState extends State { TargetPlatform.android: ZoomPageTransitionsBuilder(), }); Intl.defaultLocale = localeSelector(state); + final locale = AppLocalization.createLocale(localeSelector(state)); - return MaterialApp( - supportedLocales: kLanguages - .map((String locale) => AppLocalization.createLocale(locale)) - .toList(), - //debugShowCheckedModeBanner: false, - //showPerformanceOverlay: true, - localizationsDelegates: [ - const AppLocalizationsDelegate(), - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalMaterialLocalizations.delegate - ], - home: state.prefState.requireAuthentication && !_authenticated - ? LockScreen(onAuthenticatePressed: _authenticate) - : InitScreen(), - locale: AppLocalization.createLocale(localeSelector(state)), - theme: state.prefState.enableDarkMode - ? ThemeData( - pageTransitionsTheme: pageTransitionsTheme, - brightness: Brightness.dark, - accentColor: accentColor, - indicatorColor: accentColor, - textSelectionHandleColor: accentColor, - fontFamily: fontFamily, - backgroundColor: Colors.black, - canvasColor: Colors.black, - cardColor: const Color(0xFF1B1C1E), - bottomAppBarColor: const Color(0xFF1B1C1E), - primaryColorDark: Colors.black, - buttonColor: accentColor, - ) - : ThemeData( - pageTransitionsTheme: pageTransitionsTheme, - primaryColor: accentColor, - accentColor: accentColor, - indicatorColor: accentColor, - textSelectionColor: accentColor, - fontFamily: fontFamily, - backgroundColor: Colors.white, - canvasColor: Colors.white, - cardColor: Colors.white, - bottomAppBarColor: Colors.white, - primaryColorDark: - hasAccentColor ? accentColor : const Color(0xFF0D5D91), - primaryColorLight: - hasAccentColor ? accentColor : const Color(0xFF5dabf4), - buttonColor: - hasAccentColor ? accentColor : const Color(0xFF0D5D91), - scaffoldBackgroundColor: const Color(0xFFE7EBEE), - tabBarTheme: TabBarTheme( - labelColor: hasAccentColor ? Colors.white : Colors.black, - unselectedLabelColor: hasAccentColor - ? Colors.white.withOpacity(.65) - : Colors.black.withOpacity(.65), - ), - /* - buttonTheme: ButtonThemeData( - textTheme: ButtonTextTheme.primary, - colorScheme: ColorScheme.light( - //primary: hasAccentColor ? Colors.white : Colors.black, + return StyledToast( + locale: locale, + duration: Duration(seconds: 3), + backgroundColor: + state.prefState.enableDarkMode ? Colors.white : Colors.black, + textStyle: TextStyle( + color: + state.prefState.enableDarkMode ? Colors.black87 : Colors.white, + ), + child: MaterialApp( + supportedLocales: kLanguages + .map((String locale) => AppLocalization.createLocale(locale)) + .toList(), + //debugShowCheckedModeBanner: false, + //showPerformanceOverlay: true, + localizationsDelegates: [ + const AppLocalizationsDelegate(), + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalMaterialLocalizations.delegate + ], + home: state.prefState.requireAuthentication && !_authenticated + ? LockScreen(onAuthenticatePressed: _authenticate) + : InitScreen(), + locale: locale, + theme: state.prefState.enableDarkMode + ? ThemeData( + pageTransitionsTheme: pageTransitionsTheme, + brightness: Brightness.dark, + accentColor: accentColor, + indicatorColor: accentColor, + textSelectionHandleColor: accentColor, + fontFamily: fontFamily, + backgroundColor: Colors.black, + canvasColor: Colors.black, + cardColor: const Color(0xFF1B1C1E), + bottomAppBarColor: const Color(0xFF1B1C1E), + primaryColorDark: Colors.black, + buttonColor: accentColor, + ) + : ThemeData( + pageTransitionsTheme: pageTransitionsTheme, + primaryColor: accentColor, + accentColor: accentColor, + indicatorColor: accentColor, + textSelectionColor: accentColor, + fontFamily: fontFamily, + backgroundColor: Colors.white, + canvasColor: Colors.white, + cardColor: Colors.white, + bottomAppBarColor: Colors.white, + primaryColorDark: + hasAccentColor ? accentColor : const Color(0xFF0D5D91), + primaryColorLight: + hasAccentColor ? accentColor : const Color(0xFF5dabf4), + buttonColor: + hasAccentColor ? accentColor : const Color(0xFF0D5D91), + scaffoldBackgroundColor: const Color(0xFFE7EBEE), + tabBarTheme: TabBarTheme( + labelColor: hasAccentColor ? Colors.white : Colors.black, + unselectedLabelColor: hasAccentColor + ? Colors.white.withOpacity(.65) + : Colors.black.withOpacity(.65), ), - ), - */ - iconTheme: IconThemeData( - color: hasAccentColor ? null : accentColor, - ), - appBarTheme: AppBarTheme( - brightness: Brightness.light, - color: hasAccentColor ? accentColor : Colors.white, - iconTheme: IconThemeData( - color: hasAccentColor ? Colors.white : accentColor, - ), - textTheme: TextTheme( - headline6: Theme.of(context).textTheme.headline6.copyWith( - color: hasAccentColor ? Colors.white : Colors.black, - ), - ), - ), - ), - title: kAppName, - onGenerateRoute: isMobile(context) ? null : generateRoute, - routes: isMobile(context) - ? { - LoginScreen.route: (context) => LoginScreen(), - MainScreen.route: (context) => MainScreen(), - DashboardScreenBuilder.route: (context) => ChangeLayoutBanner( - suggestedLayout: AppLayout.mobile, - appLayout: state.prefState.appLayout, - child: DashboardScreenBuilder(), + /* + buttonTheme: ButtonThemeData( + textTheme: ButtonTextTheme.primary, + colorScheme: ColorScheme.light( + //primary: hasAccentColor ? Colors.white : Colors.black, ), - ProductScreen.route: (context) => ProductScreenBuilder(), - ProductViewScreen.route: (context) => ProductViewScreen(), - ProductEditScreen.route: (context) => ProductEditScreen(), - ClientScreen.route: (context) => ClientScreenBuilder(), - ClientViewScreen.route: (context) => ClientViewScreen(), - ClientEditScreen.route: (context) => ClientEditScreen(), - InvoiceScreen.route: (context) => InvoiceScreenBuilder(), - InvoiceViewScreen.route: (context) => InvoiceViewScreen(), - InvoiceEditScreen.route: (context) => InvoiceEditScreen(), - InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(), - DocumentScreen.route: (context) => DocumentScreenBuilder(), - DocumentViewScreen.route: (context) => DocumentViewScreen(), - DocumentEditScreen.route: (context) => DocumentEditScreen(), - ExpenseScreen.route: (context) => ExpenseScreenBuilder(), - ExpenseViewScreen.route: (context) => ExpenseViewScreen(), - ExpenseEditScreen.route: (context) => ExpenseEditScreen(), - VendorScreen.route: (context) => VendorScreenBuilder(), - VendorViewScreen.route: (context) => VendorViewScreen(), - VendorEditScreen.route: (context) => VendorEditScreen(), - TaskScreen.route: (context) => TaskScreenBuilder(), - TaskViewScreen.route: (context) => TaskViewScreen(), - TaskEditScreen.route: (context) => TaskEditScreen(), - ProjectScreen.route: (context) => ProjectScreenBuilder(), - ProjectViewScreen.route: (context) => ProjectViewScreen(), - ProjectEditScreen.route: (context) => ProjectEditScreen(), - PaymentScreen.route: (context) => PaymentScreenBuilder(), - PaymentViewScreen.route: (context) => PaymentViewScreen(), - PaymentEditScreen.route: (context) => PaymentEditScreen(), - PaymentRefundScreen.route: (context) => PaymentRefundScreen(), - QuoteScreen.route: (context) => QuoteScreenBuilder(), - QuoteViewScreen.route: (context) => QuoteViewScreen(), - QuoteEditScreen.route: (context) => QuoteEditScreen(), - QuoteEmailScreen.route: (context) => QuoteEmailScreen(), - // STARTER: routes - do not remove comment - TaskStatusScreen.route: (context) => - TaskStatusScreenBuilder(), - TaskStatusViewScreen.route: (context) => - TaskStatusViewScreen(), - TaskStatusEditScreen.route: (context) => - TaskStatusEditScreen(), - ExpenseCategoryScreen.route: (context) => - ExpenseCategoryScreenBuilder(), - ExpenseCategoryViewScreen.route: (context) => - ExpenseCategoryViewScreen(), - ExpenseCategoryEditScreen.route: (context) => - ExpenseCategoryEditScreen(), - RecurringInvoiceScreen.route: (context) => - RecurringInvoiceScreenBuilder(), - RecurringInvoiceViewScreen.route: (context) => - RecurringInvoiceViewScreen(), - RecurringInvoiceEditScreen.route: (context) => - RecurringInvoiceEditScreen(), - WebhookScreen.route: (context) => WebhookScreenBuilder(), - WebhookViewScreen.route: (context) => WebhookViewScreen(), - WebhookEditScreen.route: (context) => WebhookEditScreen(), - TokenScreen.route: (context) => TokenScreenBuilder(), - TokenViewScreen.route: (context) => TokenViewScreen(), - TokenEditScreen.route: (context) => TokenEditScreen(), - PaymentTermScreen.route: (context) => - PaymentTermScreenBuilder(), - PaymentTermEditScreen.route: (context) => - PaymentTermEditScreen(), - PaymentTermViewScreen.route: (context) => - PaymentTermViewScreen(), - DesignScreen.route: (context) => DesignScreenBuilder(), - DesignViewScreen.route: (context) => DesignViewScreen(), - DesignEditScreen.route: (context) => DesignEditScreen(), - CreditScreen.route: (context) => CreditScreenBuilder(), - CreditViewScreen.route: (context) => CreditViewScreen(), - CreditEditScreen.route: (context) => CreditEditScreen(), - UserScreen.route: (context) => UserScreenBuilder(), - UserViewScreen.route: (context) => UserViewScreen(), - UserEditScreen.route: (context) => UserEditScreen(), - GroupSettingsScreen.route: (context) => GroupScreenBuilder(), - GroupViewScreen.route: (context) => GroupViewScreen(), - GroupEditScreen.route: (context) => GroupEditScreen(), - SettingsScreen.route: (context) => SettingsScreenBuilder(), - ReportsScreen.route: (context) => ReportsScreenBuilder(), - CompanyDetailsScreen.route: (context) => - CompanyDetailsScreen(), - UserDetailsScreen.route: (context) => UserDetailsScreen(), - LocalizationScreen.route: (context) => LocalizationScreen(), - OnlinePaymentsScreen.route: (context) => - OnlinePaymentsScreen(), - CompanyGatewayScreen.route: (context) => - CompanyGatewayScreenBuilder(), - CompanyGatewayViewScreen.route: (context) => - CompanyGatewayViewScreen(), - CompanyGatewayEditScreen.route: (context) => - CompanyGatewayEditScreen(), - TaxSettingsScreen.route: (context) => TaxSettingsScreen(), - TaxRateSettingsScreen.route: (context) => - TaxRateScreenBuilder(), - TaxRateViewScreen.route: (context) => TaxRateViewScreen(), - TaxRateEditScreen.route: (context) => TaxRateEditScreen(), - ProductSettingsScreen.route: (context) => - ProductSettingsScreen(), - ExpenseSettingsScreen.route: (context) => - ExpenseSettingsScreen(), - TaskSettingsScreen.route: (context) => TaskSettingsScreen(), - IntegrationSettingsScreen.route: (context) => - IntegrationSettingsScreen(), - ImportExportScreen.route: (context) => ImportExportScreen(), - DeviceSettingsScreen.route: (context) => - DeviceSettingsScreen(), - AccountManagementScreen.route: (context) => - AccountManagementScreen(), - CustomFieldsScreen.route: (context) => CustomFieldsScreen(), - GeneratedNumbersScreen.route: (context) => - GeneratedNumbersScreen(), - WorkflowSettingsScreen.route: (context) => - WorkflowSettingsScreen(), - InvoiceDesignScreen.route: (context) => InvoiceDesignScreen(), - ClientPortalScreen.route: (context) => ClientPortalScreen(), - BuyNowButtonsScreen.route: (context) => BuyNowButtonsScreen(), - EmailSettingsScreen.route: (context) => EmailSettingsScreen(), - TemplatesAndRemindersScreen.route: (context) => - TemplatesAndRemindersScreen(), - CreditCardsAndBanksScreen.route: (context) => - CreditCardsAndBanksScreen(), - DataVisualizationsScreen.route: (context) => - DataVisualizationsScreen(), - } - : {}, + ), + */ + iconTheme: IconThemeData( + color: hasAccentColor ? null : accentColor, + ), + appBarTheme: AppBarTheme( + brightness: Brightness.light, + color: hasAccentColor ? accentColor : Colors.white, + iconTheme: IconThemeData( + color: hasAccentColor ? Colors.white : accentColor, + ), + textTheme: TextTheme( + headline6: Theme.of(context) + .textTheme + .headline6 + .copyWith( + color: + hasAccentColor ? Colors.white : Colors.black, + ), + ), + ), + ), + title: kAppName, + onGenerateRoute: isMobile(context) ? null : generateRoute, + routes: isMobile(context) + ? { + LoginScreen.route: (context) => LoginScreen(), + MainScreen.route: (context) => MainScreen(), + DashboardScreenBuilder.route: (context) => + ChangeLayoutBanner( + suggestedLayout: AppLayout.mobile, + appLayout: state.prefState.appLayout, + child: DashboardScreenBuilder(), + ), + ProductScreen.route: (context) => ProductScreenBuilder(), + ProductViewScreen.route: (context) => ProductViewScreen(), + ProductEditScreen.route: (context) => ProductEditScreen(), + ClientScreen.route: (context) => ClientScreenBuilder(), + ClientViewScreen.route: (context) => ClientViewScreen(), + ClientEditScreen.route: (context) => ClientEditScreen(), + InvoiceScreen.route: (context) => InvoiceScreenBuilder(), + InvoiceViewScreen.route: (context) => InvoiceViewScreen(), + InvoiceEditScreen.route: (context) => InvoiceEditScreen(), + InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(), + DocumentScreen.route: (context) => DocumentScreenBuilder(), + DocumentViewScreen.route: (context) => DocumentViewScreen(), + DocumentEditScreen.route: (context) => DocumentEditScreen(), + ExpenseScreen.route: (context) => ExpenseScreenBuilder(), + ExpenseViewScreen.route: (context) => ExpenseViewScreen(), + ExpenseEditScreen.route: (context) => ExpenseEditScreen(), + VendorScreen.route: (context) => VendorScreenBuilder(), + VendorViewScreen.route: (context) => VendorViewScreen(), + VendorEditScreen.route: (context) => VendorEditScreen(), + TaskScreen.route: (context) => TaskScreenBuilder(), + TaskViewScreen.route: (context) => TaskViewScreen(), + TaskEditScreen.route: (context) => TaskEditScreen(), + ProjectScreen.route: (context) => ProjectScreenBuilder(), + ProjectViewScreen.route: (context) => ProjectViewScreen(), + ProjectEditScreen.route: (context) => ProjectEditScreen(), + PaymentScreen.route: (context) => PaymentScreenBuilder(), + PaymentViewScreen.route: (context) => PaymentViewScreen(), + PaymentEditScreen.route: (context) => PaymentEditScreen(), + PaymentRefundScreen.route: (context) => + PaymentRefundScreen(), + QuoteScreen.route: (context) => QuoteScreenBuilder(), + QuoteViewScreen.route: (context) => QuoteViewScreen(), + QuoteEditScreen.route: (context) => QuoteEditScreen(), + QuoteEmailScreen.route: (context) => QuoteEmailScreen(), + // STARTER: routes - do not remove comment + TaskStatusScreen.route: (context) => + TaskStatusScreenBuilder(), + TaskStatusViewScreen.route: (context) => + TaskStatusViewScreen(), + TaskStatusEditScreen.route: (context) => + TaskStatusEditScreen(), + ExpenseCategoryScreen.route: (context) => + ExpenseCategoryScreenBuilder(), + ExpenseCategoryViewScreen.route: (context) => + ExpenseCategoryViewScreen(), + ExpenseCategoryEditScreen.route: (context) => + ExpenseCategoryEditScreen(), + RecurringInvoiceScreen.route: (context) => + RecurringInvoiceScreenBuilder(), + RecurringInvoiceViewScreen.route: (context) => + RecurringInvoiceViewScreen(), + RecurringInvoiceEditScreen.route: (context) => + RecurringInvoiceEditScreen(), + WebhookScreen.route: (context) => WebhookScreenBuilder(), + WebhookViewScreen.route: (context) => WebhookViewScreen(), + WebhookEditScreen.route: (context) => WebhookEditScreen(), + TokenScreen.route: (context) => TokenScreenBuilder(), + TokenViewScreen.route: (context) => TokenViewScreen(), + TokenEditScreen.route: (context) => TokenEditScreen(), + PaymentTermScreen.route: (context) => + PaymentTermScreenBuilder(), + PaymentTermEditScreen.route: (context) => + PaymentTermEditScreen(), + PaymentTermViewScreen.route: (context) => + PaymentTermViewScreen(), + DesignScreen.route: (context) => DesignScreenBuilder(), + DesignViewScreen.route: (context) => DesignViewScreen(), + DesignEditScreen.route: (context) => DesignEditScreen(), + CreditScreen.route: (context) => CreditScreenBuilder(), + CreditViewScreen.route: (context) => CreditViewScreen(), + CreditEditScreen.route: (context) => CreditEditScreen(), + UserScreen.route: (context) => UserScreenBuilder(), + UserViewScreen.route: (context) => UserViewScreen(), + UserEditScreen.route: (context) => UserEditScreen(), + GroupSettingsScreen.route: (context) => + GroupScreenBuilder(), + GroupViewScreen.route: (context) => GroupViewScreen(), + GroupEditScreen.route: (context) => GroupEditScreen(), + SettingsScreen.route: (context) => SettingsScreenBuilder(), + ReportsScreen.route: (context) => ReportsScreenBuilder(), + CompanyDetailsScreen.route: (context) => + CompanyDetailsScreen(), + UserDetailsScreen.route: (context) => UserDetailsScreen(), + LocalizationScreen.route: (context) => LocalizationScreen(), + OnlinePaymentsScreen.route: (context) => + OnlinePaymentsScreen(), + CompanyGatewayScreen.route: (context) => + CompanyGatewayScreenBuilder(), + CompanyGatewayViewScreen.route: (context) => + CompanyGatewayViewScreen(), + CompanyGatewayEditScreen.route: (context) => + CompanyGatewayEditScreen(), + TaxSettingsScreen.route: (context) => TaxSettingsScreen(), + TaxRateSettingsScreen.route: (context) => + TaxRateScreenBuilder(), + TaxRateViewScreen.route: (context) => TaxRateViewScreen(), + TaxRateEditScreen.route: (context) => TaxRateEditScreen(), + ProductSettingsScreen.route: (context) => + ProductSettingsScreen(), + ExpenseSettingsScreen.route: (context) => + ExpenseSettingsScreen(), + TaskSettingsScreen.route: (context) => TaskSettingsScreen(), + IntegrationSettingsScreen.route: (context) => + IntegrationSettingsScreen(), + ImportExportScreen.route: (context) => ImportExportScreen(), + DeviceSettingsScreen.route: (context) => + DeviceSettingsScreen(), + AccountManagementScreen.route: (context) => + AccountManagementScreen(), + CustomFieldsScreen.route: (context) => CustomFieldsScreen(), + GeneratedNumbersScreen.route: (context) => + GeneratedNumbersScreen(), + WorkflowSettingsScreen.route: (context) => + WorkflowSettingsScreen(), + InvoiceDesignScreen.route: (context) => + InvoiceDesignScreen(), + ClientPortalScreen.route: (context) => ClientPortalScreen(), + BuyNowButtonsScreen.route: (context) => + BuyNowButtonsScreen(), + EmailSettingsScreen.route: (context) => + EmailSettingsScreen(), + TemplatesAndRemindersScreen.route: (context) => + TemplatesAndRemindersScreen(), + CreditCardsAndBanksScreen.route: (context) => + CreditCardsAndBanksScreen(), + DataVisualizationsScreen.route: (context) => + DataVisualizationsScreen(), + } + : {}, + ), ); }), ); diff --git a/lib/ui/app/buttons/bottom_buttons.dart b/lib/ui/app/buttons/bottom_buttons.dart index 71882a589..58c707f4f 100644 --- a/lib/ui/app/buttons/bottom_buttons.dart +++ b/lib/ui/app/buttons/bottom_buttons.dart @@ -40,7 +40,9 @@ class BottomButtons extends StatelessWidget { children: [ Expanded( child: InkWell( - onTap: action1Enabled && !entity.isDeleted + onTap: action1Enabled && + (!entity.isDeleted || + action1 == EntityAction.viewPdf) ? () { handleEntityAction(context, entity, action1); } diff --git a/lib/utils/completers.dart b/lib/utils/completers.dart index 1e0f35441..1a746819f 100644 --- a/lib/utils/completers.dart +++ b/lib/utils/completers.dart @@ -1,51 +1,18 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; -import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; - -/* -Completer refreshCompleter(BuildContext context) { - final Completer completer = Completer(); - - completer.future.then((_) { - Scaffold.of(context).showSnackBar(SnackBar( - content: SnackBarRow( - message: AppLocalization.of(context).refreshComplete, - ))); - }).catchError((Object error) { - showDialog( - context: context, - builder: (BuildContext context) { - return ErrorDialog(error); - }); - }); - - return completer; -} -*/ Completer snackBarCompleter(BuildContext context, String message, {bool shouldPop = false}) { final Completer completer = Completer(); - ScaffoldState scaffold; - - try { - scaffold = Scaffold.of(context); - } catch (e) { - // - } completer.future.then((_) { if (shouldPop && Navigator.of(context).canPop()) { Navigator.of(context).pop(); } - if (scaffold != null) { - scaffold.showSnackBar(SnackBar( - content: SnackBarRow( - message: message, - ))); - } + showToast(message); }).catchError((Object error) { if (shouldPop && Navigator.of(context).canPop()) { Navigator.of(context).pop(); diff --git a/pubspec.lock b/pubspec.lock index 35b7c7683..93ce142bd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -415,6 +415,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.5.7" + flutter_styled_toast: + dependency: "direct main" + description: + name: flutter_styled_toast + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0+1" flutter_test: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 0cf64134e..816e9d613 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: #idb_shim: ^1.11.1+1 flutter_launcher_icons: ^0.8.0 overflow_view: ^0.2.1 + flutter_styled_toast: ^1.4.0+1 dev_dependencies: flutter_driver: