diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index b52183a..2a2d082 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,71 +1,58 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> + localPropertiesFile.withReader("UTF-8") { reader -> localProperties.load(reader) } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = "1" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') +def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = "1.0" } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + namespace = "com.example.example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" + applicationId = "com.example.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + source = "../.." } diff --git a/example/android/build.gradle b/example/android/build.gradle index 713d7f6..d2ffbff 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -18,12 +5,12 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bc..536165d 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/example/lib/main.dart b/example/lib/main.dart index a5191e7..d801daa 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -104,17 +104,14 @@ class MyAppState extends State { onChanged: print, // Initial selection and favorite can be one of code ('IT') OR dial_code('+39') initialSelection: 'IT', - favorite: const ['+39', 'FR'], - countryFilter: const ['IT', 'FR'], - showFlagDialog: false, //You can set the margin between the flag and the country name to your taste. margin: const EdgeInsets.symmetric(horizontal: 6), comparator: (a, b) => b.name!.compareTo(a.name!), //Get the country information relevant to the initial selection - onInit: (code) => debugPrint( - "on init ${code?.name} ${code?.dialCode} ${code?.name}"), + onInit: (code) => debugPrint("on init ${code?.name} ${code?.dialCode} ${code?.name}"), ), CountryCodePicker( + hideHeaderText: true, onChanged: print, // Initial selection and favorite can be one of code ('IT') OR dial_code('+39') initialSelection: 'IT', diff --git a/lib/country_code_picker.dart b/lib/country_code_picker.dart index 1a3f871..4bde20d 100644 --- a/lib/country_code_picker.dart +++ b/lib/country_code_picker.dart @@ -93,6 +93,21 @@ class CountryCodePicker extends StatefulWidget { final EdgeInsetsGeometry searchPadding; + ///Use This To Hide The Header Text + final bool hideHeaderText; + + ///Change The Header Text + final String? headerText; + + ///Header Text Style + final TextStyle headerTextStyle; + + ///Header Text Padding + final EdgeInsets topBarPadding; + + ///Header Text Alignment + final MainAxisAlignment headerAlignment; + const CountryCodePicker({ this.onChanged, this.onInit, @@ -129,9 +144,13 @@ class CountryCodePicker extends StatefulWidget { this.dialogBackgroundColor, this.closeIcon = const Icon(Icons.close), this.countryList = codes, - this.dialogItemPadding = - const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + this.dialogItemPadding = const EdgeInsets.symmetric(horizontal: 24, vertical: 8), this.searchPadding = const EdgeInsets.symmetric(horizontal: 24), + this.headerAlignment = MainAxisAlignment.spaceBetween, + this.headerText = "Select County", + this.headerTextStyle = const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + this.hideHeaderText = false, + this.topBarPadding = const EdgeInsets.symmetric(vertical: 5.0, horizontal: 20), Key? key, }) : super(key: key); @@ -140,22 +159,15 @@ class CountryCodePicker extends StatefulWidget { State createState() { List> jsonList = countryList; - List elements = - jsonList.map((json) => CountryCode.fromJson(json)).toList(); + List elements = jsonList.map((json) => CountryCode.fromJson(json)).toList(); if (comparator != null) { elements.sort(comparator); } if (countryFilter != null && countryFilter!.isNotEmpty) { - final uppercaseCustomList = - countryFilter!.map((criteria) => criteria.toUpperCase()).toList(); - elements = elements - .where((criteria) => - uppercaseCustomList.contains(criteria.code) || - uppercaseCustomList.contains(criteria.name) || - uppercaseCustomList.contains(criteria.dialCode)) - .toList(); + final uppercaseCustomList = countryFilter!.map((criteria) => criteria.toUpperCase()).toList(); + elements = elements.where((criteria) => uppercaseCustomList.contains(criteria.code) || uppercaseCustomList.contains(criteria.name) || uppercaseCustomList.contains(criteria.dialCode)).toList(); } return CountryCodePickerState(elements); @@ -186,21 +198,14 @@ class CountryCodePickerState extends State { direction: Axis.horizontal, mainAxisSize: MainAxisSize.min, children: [ - if (widget.showFlagMain != null - ? widget.showFlagMain! - : widget.showFlag) + if (widget.showFlagMain != null ? widget.showFlagMain! : widget.showFlag) Flexible( flex: widget.alignLeft ? 0 : 1, fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, child: Container( - clipBehavior: widget.flagDecoration == null - ? Clip.none - : Clip.hardEdge, + clipBehavior: widget.flagDecoration == null ? Clip.none : Clip.hardEdge, decoration: widget.flagDecoration, - margin: widget.margin ?? - (widget.alignLeft - ? const EdgeInsets.only(right: 16.0, left: 8.0) - : const EdgeInsets.only(right: 16.0)), + margin: widget.margin ?? (widget.alignLeft ? const EdgeInsets.only(right: 16.0, left: 8.0) : const EdgeInsets.only(right: 16.0)), child: Image.asset( selectedItem!.flagUri!, package: 'country_code_picker', @@ -212,11 +217,8 @@ class CountryCodePickerState extends State { Flexible( fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, child: Text( - widget.showOnlyCountryWhenClosed - ? selectedItem!.toCountryStringOnly() - : selectedItem.toString(), - style: widget.textStyle ?? - Theme.of(context).textTheme.labelLarge, + widget.showOnlyCountryWhenClosed ? selectedItem!.toCountryStringOnly() : selectedItem.toString(), + style: widget.textStyle ?? Theme.of(context).textTheme.labelLarge, overflow: widget.textOverflow, ), ), @@ -225,9 +227,7 @@ class CountryCodePickerState extends State { flex: widget.alignLeft ? 0 : 1, fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, child: Padding( - padding: (widget.alignLeft - ? const EdgeInsets.only(right: 16.0, left: 8.0) - : const EdgeInsets.only(right: 16.0)), + padding: (widget.alignLeft ? const EdgeInsets.only(right: 16.0, left: 8.0) : const EdgeInsets.only(right: 16.0)), child: Icon( Icons.arrow_drop_down, color: Colors.grey, @@ -258,11 +258,9 @@ class CountryCodePickerState extends State { if (widget.initialSelection != null) { selectedItem = elements.firstWhere( (criteria) => - (criteria.code!.toUpperCase() == - widget.initialSelection!.toUpperCase()) || + (criteria.code!.toUpperCase() == widget.initialSelection!.toUpperCase()) || (criteria.dialCode == widget.initialSelection) || - (criteria.name!.toUpperCase() == - widget.initialSelection!.toUpperCase()), + (criteria.name!.toUpperCase() == widget.initialSelection!.toUpperCase()), orElse: () => elements[0]); } else { selectedItem = elements[0]; @@ -278,11 +276,9 @@ class CountryCodePickerState extends State { if (widget.initialSelection != null) { selectedItem = elements.firstWhere( (item) => - (item.code!.toUpperCase() == - widget.initialSelection!.toUpperCase()) || + (item.code!.toUpperCase() == widget.initialSelection!.toUpperCase()) || (item.dialCode == widget.initialSelection) || - (item.name!.toUpperCase() == - widget.initialSelection!.toUpperCase()), + (item.name!.toUpperCase() == widget.initialSelection!.toUpperCase()), orElse: () => elements[0]); } else { selectedItem = elements[0]; @@ -290,10 +286,7 @@ class CountryCodePickerState extends State { favoriteElements = elements .where((item) => - widget.favorite.firstWhereOrNull((criteria) => - item.code!.toUpperCase() == criteria.toUpperCase() || - item.dialCode == criteria || - item.name!.toUpperCase() == criteria.toUpperCase()) != + widget.favorite.firstWhereOrNull((criteria) => item.code!.toUpperCase() == criteria.toUpperCase() || item.dialCode == criteria || item.name!.toUpperCase() == criteria.toUpperCase()) != null) .toList(); } @@ -316,6 +309,11 @@ class CountryCodePickerState extends State { showFlag: widget.showFlagDialog ?? widget.showFlag, flagWidth: widget.flagWidth, size: widget.dialogSize, + headerAlignment: widget.headerAlignment, + headerText: widget.headerText, + headerTextStyle: widget.headerTextStyle, + hideHeaderText: widget.hideHeaderText, + topBarPadding: widget.topBarPadding, backgroundColor: widget.dialogBackgroundColor, barrierColor: widget.barrierColor, hideSearch: widget.hideSearch, diff --git a/lib/src/selection_dialog.dart b/lib/src/selection_dialog.dart index 6f932fe..58b1f97 100644 --- a/lib/src/selection_dialog.dart +++ b/lib/src/selection_dialog.dart @@ -10,6 +10,7 @@ class SelectionDialog extends StatefulWidget { final InputDecoration searchDecoration; final TextStyle? searchStyle; final TextStyle? textStyle; + final TextStyle headerTextStyle; final BoxDecoration? boxDecoration; final WidgetBuilder? emptySearchBuilder; final bool? showFlag; @@ -19,6 +20,10 @@ class SelectionDialog extends StatefulWidget { final bool hideSearch; final bool hideCloseIcon; final Icon? closeIcon; + final bool hideHeaderText; + final String? headerText; + final EdgeInsets topBarPadding; + final MainAxisAlignment headerAlignment; /// Background color of SelectionDialog final Color? backgroundColor; @@ -38,10 +43,15 @@ class SelectionDialog extends StatefulWidget { this.favoriteElements, { Key? key, this.showCountryOnly, + required this.hideHeaderText, this.emptySearchBuilder, + required this.headerAlignment, + required this.headerTextStyle, InputDecoration searchDecoration = const InputDecoration(), this.searchStyle, this.textStyle, + required this.topBarPadding, + this.headerText, this.boxDecoration, this.showFlag, this.flagDecoration, @@ -54,9 +64,7 @@ class SelectionDialog extends StatefulWidget { this.closeIcon, this.dialogItemPadding = const EdgeInsets.symmetric(horizontal: 24, vertical: 8), this.searchPadding = const EdgeInsets.symmetric(horizontal: 24), - }) : searchDecoration = searchDecoration.prefixIcon == null - ? searchDecoration.copyWith(prefixIcon: const Icon(Icons.search)) - : searchDecoration, + }) : searchDecoration = searchDecoration.prefixIcon == null ? searchDecoration.copyWith(prefixIcon: const Icon(Icons.search)) : searchDecoration, super(key: key); @override @@ -73,8 +81,7 @@ class _SelectionDialogState extends State { child: Container( clipBehavior: Clip.hardEdge, width: widget.size?.width ?? MediaQuery.of(context).size.width, - height: - widget.size?.height ?? MediaQuery.of(context).size.height * 0.85, + height: widget.size?.height ?? MediaQuery.of(context).size.height * 0.85, decoration: widget.boxDecoration ?? BoxDecoration( color: widget.backgroundColor ?? Colors.white, @@ -92,12 +99,27 @@ class _SelectionDialogState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ - if (!widget.hideCloseIcon) - IconButton( - padding: const EdgeInsets.all(0), - iconSize: 20, - icon: widget.closeIcon!, - onPressed: () => Navigator.pop(context), + Padding( + padding:!widget.hideHeaderText? widget.topBarPadding: EdgeInsets.zero, + child: Row( + mainAxisAlignment: widget.headerAlignment, + children: [ + !widget.hideHeaderText && widget.headerText != null + ? Text( + widget.headerText!, + overflow: TextOverflow.fade, + style: widget.headerTextStyle, + ) + : const SizedBox.shrink(), + if (!widget.hideCloseIcon) + IconButton( + padding: const EdgeInsets.all(0), + iconSize: 20, + icon: widget.closeIcon!, + onPressed: () => Navigator.pop(context), + ), + ], + ), ), if (!widget.hideSearch) Padding( @@ -116,34 +138,28 @@ class _SelectionDialogState extends State { : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ...widget.favoriteElements.map( - (f) => InkWell( + ...widget.favoriteElements.map((f) => InkWell( onTap: () { _selectItem(f); }, child: Padding( padding: widget.dialogItemPadding, child: _buildOption(f), - ) - ) - ), + ))), const Divider(), ], ), if (filteredElements.isEmpty) _buildEmptySearchWidget(context) else - ...filteredElements.map( - (e) => InkWell( + ...filteredElements.map((e) => InkWell( onTap: () { _selectItem(e); }, child: Padding( - padding: widget.dialogItemPadding, + padding: widget.dialogItemPadding, child: _buildOption(e), - ) - ) - ), + ))), ], ), ), @@ -161,10 +177,11 @@ class _SelectionDialogState extends State { if (widget.showFlag!) Flexible( child: Container( - margin: const EdgeInsets.only(right: 16.0), + margin: Directionality.of(context) == TextDirection.ltr // Here Adding padding depending on the locale language direction + ? const EdgeInsets.only(right: 16.0) + : const EdgeInsets.only(left: 16.0), decoration: widget.flagDecoration, - clipBehavior: - widget.flagDecoration == null ? Clip.none : Clip.hardEdge, + clipBehavior: widget.flagDecoration == null ? Clip.none : Clip.hardEdge, child: Image.asset( e.flagUri!, package: 'country_code_picker', @@ -175,9 +192,7 @@ class _SelectionDialogState extends State { Expanded( flex: 4, child: Text( - widget.showCountryOnly! - ? e.toCountryStringOnly() - : e.toLongString(), + widget.showCountryOnly! ? e.toCountryStringOnly() : e.toLongString(), overflow: TextOverflow.fade, style: widget.textStyle, ), @@ -193,8 +208,7 @@ class _SelectionDialogState extends State { } return Center( - child: Text(CountryLocalizations.of(context)?.translate('no_country') ?? - 'No country found'), + child: Text(CountryLocalizations.of(context)?.translate('no_country') ?? 'No country found'), ); } @@ -207,12 +221,7 @@ class _SelectionDialogState extends State { void _filterElements(String s) { s = s.toUpperCase(); setState(() { - filteredElements = widget.elements - .where((e) => - e.code!.contains(s) || - e.dialCode!.contains(s) || - e.name!.toUpperCase().contains(s)) - .toList(); + filteredElements = widget.elements.where((e) => e.code!.contains(s) || e.dialCode!.contains(s) || e.name!.toUpperCase().contains(s)).toList(); }); } diff --git a/screenshots/screen1.png b/screenshots/screen1.png index ebb6a19..2b33cf1 100644 Binary files a/screenshots/screen1.png and b/screenshots/screen1.png differ diff --git a/screenshots/screen2.png b/screenshots/screen2.png index 9c02d84..ea35c36 100644 Binary files a/screenshots/screen2.png and b/screenshots/screen2.png differ