├── .flutter-plugins-dependencies ├── .github └── workflows │ └── pub.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .flutter-plugins-dependencies ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── dormmom │ │ │ │ │ └── flutter_twilio_voice_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── main.dart │ ├── screens │ │ ├── dialpad_basic.dart │ │ ├── dialpad_ios.dart │ │ ├── dialpad_metro.dart │ │ └── dialpad_selection.dart │ └── widgets │ │ └── ui_app_bar.dart ├── pubspec.lock ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json ├── lib ├── flutter_dialpad.dart └── src │ ├── flutter_dialpad.dart │ ├── generator │ ├── generator.dart │ ├── ios_keypad_generator.dart │ ├── keypad_indexed_generator.dart │ └── phone_keypad_generator.dart │ ├── mixins │ └── scalable.dart │ ├── models │ ├── key_value.dart │ └── models.dart │ ├── utils │ └── material_color.dart │ └── widgets │ ├── buttons │ ├── action_button.dart │ └── buttons.dart │ ├── keypad_focus_node.dart │ ├── keypad_grid.dart │ ├── phone_text_field.dart │ ├── scalable │ ├── button_type.dart │ ├── scalable.dart │ ├── scalable_button.dart │ ├── scaling_size.dart │ └── scaling_type.dart │ └── widgets.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshots ├── screenshot1.png └── screenshot2.png └── test └── flutter_dialpad_test.dart /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","native_build":true,"dependencies":[]}],"android":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_dtmf","dependencies":[]}],"date_created":"2023-11-25 00:25:51.831094","version":"3.16.0"} -------------------------------------------------------------------------------- /.github/workflows/pub.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Publish Package 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Install Flutter 22 | uses: subosito/flutter-action@v2.3.0 23 | 24 | - name: Install dependencies 25 | run: flutter pub get 26 | 27 | - name: Analyze 28 | run: flutter analyze 29 | 30 | # Your project will need to have tests in test/ and a dependency on 31 | # package:test for this step to succeed. Note that Flutter projects will 32 | # want to change this to 'flutter test'. 33 | - name: Run tests 34 | run: flutter test 35 | 36 | - name: Setup credentials 37 | run: | 38 | mkdir -p ~/.pub-cache 39 | cat < ~/.pub-cache/credentials.json 40 | { 41 | "accessToken":"${{ secrets.PUB_DEV_PUBLISH_ACCESS_TOKEN }}", 42 | "refreshToken":"${{ secrets.PUB_DEV_PUBLISH_REFRESH_TOKEN }}", 43 | "tokenEndpoint":"${{ secrets.PUB_DEV_PUBLISH_TOKEN_ENDPOINT }}", 44 | "scopes": [ "openid", "https://www.googleapis.com/auth/userinfo.email" ], 45 | "expiration": "${{ secrets.PUB_DEV_PUBLISH_EXPIRATION }} 46 | } 47 | EOF 48 | - name: Check Publish Warnings 49 | run: flutter pub publish --dry-run 50 | 51 | - name: Publish package 52 | run: flutter pub publish -f 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: b712a172f9694745f50505c93340883493b505e5 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.0] - 2023-00-23 2 | 3 | * Add custom keypad `keypad_generator`s with extensibility options. 4 | * Add `DialButton`with (future release) custom `keypadButtonBuilder` for specific usecases. 5 | * Refactor Flutter Dialpad with prebuilt UIs (iOS, metro theme styles). 6 | * Add `hideBackSpace` option. 7 | * Add `padding` fields for buttons & text input. 8 | * Changed UI scaling from height-based to width/height (thousand'th) ratio. 9 | * Add keyboard input (numpad, number row, including various phone keypad symbols & actions), see [DigitKey, ActionKey] keys. 10 | * Add `callOnEnter` to "place call" on numpad keyboard action. 11 | * Add clipboard copy from/paste to & copy to clipboard option. 12 | * Add custom text size fields for dial, button and subtitle text 13 | * Add `buttonType` button style, for circular-or-rectangle-styles. 14 | * Update example with prebuilt UIs 15 | * Custom `ScalingType` and `ScalingSize` allows for customizability with the scalar value when determining the text/widget size. 16 | * `ScalingType` determines how the viewport window dimensions are used 17 | * `ScalingSize` is an enum providing preset scalar values, allowing for more flexibility than the default `0.001` 18 | * `Scalable` mixin allows provides a `rescale` function returning the newly scaled widget/text sizes 19 | * `ScalableButton` provides inheritable scaling properties based on `MaterialButton` 20 | * Add `copyToClipboard` custom widget builder for `PhoneTextInput` 21 | * Add `dialButton` & `backspaceButton` padding 22 | * Added default & button-specific `ScalingType` and `ScalingSize` options 23 | * Add content padding for dial, backspace and keypad buttons 24 | * Add customizing dial, backspace button color & sizes 25 | * Add scale clamping as a percentage of icon/text size for improved UI/UX, with defaults to [0.2, 1.0] 26 | * Added `hideBackspaceOnEmpty` to automatically hide backspace button when input is empty 27 | * Added `onTextChanged` to capture input text changed as displayed in the `PhoneTextInput` 28 | * Add `initialText`, `withNumber` properties to autofill `PhoneTextInput` 29 | 30 | ## [1.0.5] - 2023-02-08 31 | 32 | * Add ability to hide DialButton subtitle text 33 | 34 | ## [1.0.4] - 2022-02-21 35 | 36 | * Crash Fix 37 | 38 | ## [1.0.3] - 2022-01-20 39 | 40 | * Add Key Press Event 41 | * Dtmf Library Update 42 | 43 | ## [1.0.2] - 2022-01-20 44 | 45 | * Bug Fixes 46 | 47 | ## [1.0.1] - 2021-05-01 48 | 49 | * Migrate to Null Safety 50 | 51 | ## [1.0.0] - 2020-04-19 52 | 53 | * Updated Example 54 | 55 | ## [0.0.6+2] - 2020-03-19 56 | 57 | * Dependencies Update 58 | 59 | ## [0.0.6+1] - 2019-07-28 60 | 61 | * Make Textfield for output readonly 62 | 63 | ## [0.0.6] - 2019-07-28 64 | 65 | * Added Option to not animate dial button onTap 66 | 67 | ## [0.0.5] - 2019-07-27 68 | 69 | * Fix issue where dial button event is not fired 70 | 71 | ## [0.0.4] - 2019-07-27 72 | 73 | * Added DTMF Tone and Option to Turn it Off 74 | 75 | ## [0.0.3] - 2019-07-26 76 | 77 | * Added Example and Overflow Bug Fix 78 | 79 | ## [0.0.2] - 2019-07-26 80 | 81 | * Clean up code per pub.dev suggestions 82 | 83 | ## [0.0.1] - 2019-07-26 84 | 85 | * Initial Release of the Flutter Dial Pad Widget 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Chromium Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided 11 | with the distribution. 12 | * Neither the name of Google Inc. nor the names of its 13 | contributors may be used to endorse or promote products derived 14 | from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # [flutter_dialpad](https://pub.dev/packages/flutter_dialpad) 3 | 4 | A phone dialer widget that can be added to any Flutter Application to enable the ability to dial a number. This could be combined with a VoIP application to enable placing calls. 5 | 6 | This is a purely Dart widget with no dependency on Android or iOS except for the flutter_dtmf package it uses for DTMF tone generation on pressing a button. You can turn it off by setting ```enableDtmf: false``` 7 | 8 | ## Getting Started 9 | 10 | ``` 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | backgroundColor: Colors.black, 15 | body: SafeArea( 16 | child: 17 | DialPad( 18 | // temporarily disabled 19 | // enableDtmf: true, 20 | outputMask: "(000) 000-0000", 21 | backspaceButtonIconColor: Colors.red, 22 | makeCall: (number){ 23 | print(number); 24 | } 25 | ) 26 | ), 27 | ); 28 | } 29 | ``` 30 | 31 | ## Pre-built UIs 32 | 33 | Support has been added for custom UIs, using `Dialpad.ios` for iOS style keypad or `DialPad.metro` metro theme. Additional customization is available with many different options. 34 | 35 | ## UI Scaling 36 | 37 | A few considerations: 38 | 39 | #### `ScalingType` 40 | DialPad supports dynamic scaling of UI. Using either a `ScalingType.fixed` approach will use preset text & icon sizes. If you are using [flutter_web_frame](https://pub.dev/packages/flutter_web_frame) with some window constraints, you may wish to use `ScalingType.height`. For most cases `ScalingType.min` should suffice. 41 | 42 | #### `ScalingSize` 43 | Scaling Size provides an easy way to specify the scalar multiplier for the scaling calculation. Instead of using a predefined value of e.g. 0.001, the `ScalingSize.small` is now used. Customization for buttons allows making the dial button large with a small backspace button. 44 | 45 | #### Scalable 46 | If you wish to add your own Scalable button, feel free to create a widget `with Scalable` to make use of the `rescale` function. 47 | 48 | #### Clamping 49 | 50 | Scaled UI elements may become extremely small, using `minScalingSize` and `maxScalingSize` you can clamp the scaling to a percentage of the original size. This is useful for ensuring the UI is still usable. 51 | 52 | ## Screenshots 53 | 54 | ![iOS Screenshot](screenshots/screenshot1.png?raw=true "iOS Screenshot") | ![Android Screenshot](screenshots/screenshot2.png?raw=true "Android Screenshot") 55 | |:---:|:---:| 56 | | iOS Screenshot | Android Screenshot | 57 | 58 | ## To Do 59 | [Done] Add DTMF Tones 60 | * Shrink Ouput to Fit 61 | * Support for Local Numbers in Text Input Mask 62 | -------------------------------------------------------------------------------- /example/.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","native_build":true,"dependencies":[]}],"android":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"flutter_dtmf","path":"/Users/eopeter/.pub-cache/hosted/pub.dev/flutter_dtmf-3.0.0/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_dtmf","dependencies":[]}],"date_created":"2023-11-25 00:45:12.506478","version":"3.16.0"} -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 8 | channel: unknown 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 17 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 18 | - platform: web 19 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 20 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_dialpad_example 2 | 3 | A phone dialer widget that can be added to any Flutter Application to enable ability to dial a number. This could be combined with a voip application to enable placing calls. 4 | 5 | This is a purely Dart widget with no dependency on Android or iOS except for the flutter_dtmf package it uses for DTMF tone generation on pressing a button. You can turn it off my setting ```enableDtmf: false``` 6 | 7 | ## Getting Started 8 | 9 | ``` 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | backgroundColor: Colors.black, 14 | body: SafeArea( 15 | child: 16 | DialPad( 17 | enableDtmf: true, 18 | backspaceButtonIconColor: Colors.red, 19 | makeCall: (number){ 20 | print(number); 21 | } 22 | ) 23 | ), 24 | ); 25 | } 26 | 27 | ``` 28 | 29 | ## To Do 30 | [Done] Add DTMF Tones 31 | Support Localization -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 31 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.dormmom.flutter_dialpad_example" 42 | minSdkVersion 16 43 | targetSdkVersion 31 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 66 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 67 | } 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 16 | 24 | 28 | 31 | 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/dormmom/flutter_twilio_voice_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.dormmom.flutter_dialpad_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.4.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableR8=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'A UI toolkit for beautiful and fast apps.' 10 | s.homepage = 'https://flutter.dev' 11 | s.license = { :type => 'BSD' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '11.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/eopeter/Development/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/eopeter/Projects/Flutter/Plugins/flutter_dialpad/example" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=/Users/eopeter/Projects/Flutter/Plugins/flutter_dialpad/example/lib/main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1" 10 | export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC83NGQxNjYyN2I5NDBiYjE1ZTUwODkxZjgyY2FkNmMzZTM0NjViZDZkLw==" 11 | export "DART_OBFUSCATION=false" 12 | export "TRACK_WIDGET_CREATION=true" 13 | export "TREE_SHAKE_ICONS=false" 14 | export "PACKAGE_CONFIG=/Users/eopeter/Projects/Flutter/Plugins/flutter_dialpad/example/.dart_tool/package_config.json" 15 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_dtmf (1.0.1): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - flutter_dtmf (from `.symlinks/plugins/flutter_dtmf/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | flutter_dtmf: 14 | :path: ".symlinks/plugins/flutter_dtmf/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 18 | flutter_dtmf: d86aaec3002a172abba756dc179aaf786bb82215 19 | 20 | PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 21 | 22 | COCOAPODS: 1.12.1 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | FE8193025A7DCEB6938092C5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4892E9D4B4BADD8D3622E40A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 4892E9D4B4BADD8D3622E40A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 6DAB912A5C9B9C4A6F6366B4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 39 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 40 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 42 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 43 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 44 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | B06F2DB83F3E849561A8EC57 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 50 | BB67005F34A8C77BC41FFF28 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | FE8193025A7DCEB6938092C5 /* Pods_Runner.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 38C080DFC2EC0CCE4FBA009D /* Frameworks */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 4892E9D4B4BADD8D3622E40A /* Pods_Runner.framework */, 69 | ); 70 | name = Frameworks; 71 | sourceTree = ""; 72 | }; 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 77 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 78 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 79 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 80 | ); 81 | name = Flutter; 82 | sourceTree = ""; 83 | }; 84 | 97C146E51CF9000F007C117D = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9740EEB11CF90186004384FC /* Flutter */, 88 | 97C146F01CF9000F007C117D /* Runner */, 89 | 97C146EF1CF9000F007C117D /* Products */, 90 | B0BDF7B62973BB8CD17B3FF2 /* Pods */, 91 | 38C080DFC2EC0CCE4FBA009D /* Frameworks */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 97C146EF1CF9000F007C117D /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 97C146EE1CF9000F007C117D /* Runner.app */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 97C146F01CF9000F007C117D /* Runner */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 107 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 108 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 109 | 97C147021CF9000F007C117D /* Info.plist */, 110 | 97C146F11CF9000F007C117D /* Supporting Files */, 111 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 112 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 113 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 114 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 115 | ); 116 | path = Runner; 117 | sourceTree = ""; 118 | }; 119 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | ); 123 | name = "Supporting Files"; 124 | sourceTree = ""; 125 | }; 126 | B0BDF7B62973BB8CD17B3FF2 /* Pods */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 6DAB912A5C9B9C4A6F6366B4 /* Pods-Runner.debug.xcconfig */, 130 | BB67005F34A8C77BC41FFF28 /* Pods-Runner.release.xcconfig */, 131 | B06F2DB83F3E849561A8EC57 /* Pods-Runner.profile.xcconfig */, 132 | ); 133 | path = Pods; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXNativeTarget section */ 139 | 97C146ED1CF9000F007C117D /* Runner */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 142 | buildPhases = ( 143 | A35F484619056642EE417032 /* [CP] Check Pods Manifest.lock */, 144 | 9740EEB61CF901F6004384FC /* Run Script */, 145 | 97C146EA1CF9000F007C117D /* Sources */, 146 | 97C146EB1CF9000F007C117D /* Frameworks */, 147 | 97C146EC1CF9000F007C117D /* Resources */, 148 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 149 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 150 | 374597D466E7C6DE61A3BAEF /* [CP] Embed Pods Frameworks */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = Runner; 157 | productName = Runner; 158 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 97C146E61CF9000F007C117D /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastUpgradeCheck = 1430; 168 | ORGANIZATIONNAME = "The Chromium Authors"; 169 | TargetAttributes = { 170 | 97C146ED1CF9000F007C117D = { 171 | CreatedOnToolsVersion = 7.3.1; 172 | DevelopmentTeam = U5K74N99H5; 173 | LastSwiftMigration = 0910; 174 | ProvisioningStyle = Automatic; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 204 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 205 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 374597D466E7C6DE61A3BAEF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 219 | "${BUILT_PRODUCTS_DIR}/flutter_dtmf/flutter_dtmf.framework", 220 | ); 221 | name = "[CP] Embed Pods Frameworks"; 222 | outputPaths = ( 223 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_dtmf.framework", 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 228 | showEnvVarsInLog = 0; 229 | }; 230 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 231 | isa = PBXShellScriptBuildPhase; 232 | alwaysOutOfDate = 1; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | ); 236 | inputPaths = ( 237 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 238 | ); 239 | name = "Thin Binary"; 240 | outputPaths = ( 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | shellPath = /bin/sh; 244 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 245 | }; 246 | 9740EEB61CF901F6004384FC /* Run Script */ = { 247 | isa = PBXShellScriptBuildPhase; 248 | alwaysOutOfDate = 1; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | ); 252 | inputPaths = ( 253 | ); 254 | name = "Run Script"; 255 | outputPaths = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | shellPath = /bin/sh; 259 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 260 | }; 261 | A35F484619056642EE417032 /* [CP] Check Pods Manifest.lock */ = { 262 | isa = PBXShellScriptBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | inputFileListPaths = ( 267 | ); 268 | inputPaths = ( 269 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 270 | "${PODS_ROOT}/Manifest.lock", 271 | ); 272 | name = "[CP] Check Pods Manifest.lock"; 273 | outputFileListPaths = ( 274 | ); 275 | outputPaths = ( 276 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | shellPath = /bin/sh; 280 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 281 | showEnvVarsInLog = 0; 282 | }; 283 | /* End PBXShellScriptBuildPhase section */ 284 | 285 | /* Begin PBXSourcesBuildPhase section */ 286 | 97C146EA1CF9000F007C117D /* Sources */ = { 287 | isa = PBXSourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 291 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXVariantGroup section */ 298 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 97C146FB1CF9000F007C117D /* Base */, 302 | ); 303 | name = Main.storyboard; 304 | sourceTree = ""; 305 | }; 306 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | 97C147001CF9000F007C117D /* Base */, 310 | ); 311 | name = LaunchScreen.storyboard; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXVariantGroup section */ 315 | 316 | /* Begin XCBuildConfiguration section */ 317 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ALWAYS_SEARCH_USER_PATHS = NO; 321 | CLANG_ANALYZER_NONNULL = YES; 322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 323 | CLANG_CXX_LIBRARY = "libc++"; 324 | CLANG_ENABLE_MODULES = YES; 325 | CLANG_ENABLE_OBJC_ARC = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INFINITE_RECURSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 348 | ENABLE_NS_ASSERTIONS = NO; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 359 | MTL_ENABLE_DEBUG_INFO = NO; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Profile; 365 | }; 366 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_IDENTITY = "Apple Development"; 372 | CODE_SIGN_STYLE = Automatic; 373 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 374 | DEVELOPMENT_TEAM = U5K74N99H5; 375 | ENABLE_BITCODE = NO; 376 | FRAMEWORK_SEARCH_PATHS = ( 377 | "$(inherited)", 378 | "$(PROJECT_DIR)/Flutter", 379 | ); 380 | INFOPLIST_FILE = Runner/Info.plist; 381 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 382 | LIBRARY_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "$(PROJECT_DIR)/Flutter", 385 | ); 386 | PRODUCT_BUNDLE_IDENTIFIER = com.dormmom.test; 387 | PRODUCT_NAME = "$(TARGET_NAME)"; 388 | PROVISIONING_PROFILE_SPECIFIER = ""; 389 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 390 | SWIFT_VERSION = 4.0; 391 | VERSIONING_SYSTEM = "apple-generic"; 392 | }; 393 | name = Profile; 394 | }; 395 | 97C147031CF9000F007C117D /* Debug */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | ALWAYS_SEARCH_USER_PATHS = NO; 399 | CLANG_ANALYZER_NONNULL = YES; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_MODULES = YES; 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 405 | CLANG_WARN_BOOL_CONVERSION = YES; 406 | CLANG_WARN_COMMA = YES; 407 | CLANG_WARN_CONSTANT_CONVERSION = YES; 408 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INFINITE_RECURSION = YES; 413 | CLANG_WARN_INT_CONVERSION = YES; 414 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 415 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 416 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 418 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 419 | CLANG_WARN_STRICT_PROTOTYPES = YES; 420 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 421 | CLANG_WARN_UNREACHABLE_CODE = YES; 422 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 423 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 424 | COPY_PHASE_STRIP = NO; 425 | DEBUG_INFORMATION_FORMAT = dwarf; 426 | ENABLE_STRICT_OBJC_MSGSEND = YES; 427 | ENABLE_TESTABILITY = YES; 428 | GCC_C_LANGUAGE_STANDARD = gnu99; 429 | GCC_DYNAMIC_NO_PIC = NO; 430 | GCC_NO_COMMON_BLOCKS = YES; 431 | GCC_OPTIMIZATION_LEVEL = 0; 432 | GCC_PREPROCESSOR_DEFINITIONS = ( 433 | "DEBUG=1", 434 | "$(inherited)", 435 | ); 436 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 437 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 438 | GCC_WARN_UNDECLARED_SELECTOR = YES; 439 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 440 | GCC_WARN_UNUSED_FUNCTION = YES; 441 | GCC_WARN_UNUSED_VARIABLE = YES; 442 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 443 | MTL_ENABLE_DEBUG_INFO = YES; 444 | ONLY_ACTIVE_ARCH = YES; 445 | SDKROOT = iphoneos; 446 | TARGETED_DEVICE_FAMILY = "1,2"; 447 | }; 448 | name = Debug; 449 | }; 450 | 97C147041CF9000F007C117D /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_SEARCH_USER_PATHS = NO; 454 | CLANG_ANALYZER_NONNULL = YES; 455 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 456 | CLANG_CXX_LIBRARY = "libc++"; 457 | CLANG_ENABLE_MODULES = YES; 458 | CLANG_ENABLE_OBJC_ARC = YES; 459 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 460 | CLANG_WARN_BOOL_CONVERSION = YES; 461 | CLANG_WARN_COMMA = YES; 462 | CLANG_WARN_CONSTANT_CONVERSION = YES; 463 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INFINITE_RECURSION = YES; 468 | CLANG_WARN_INT_CONVERSION = YES; 469 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 470 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 471 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 472 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 473 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 474 | CLANG_WARN_STRICT_PROTOTYPES = YES; 475 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 476 | CLANG_WARN_UNREACHABLE_CODE = YES; 477 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 478 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 479 | COPY_PHASE_STRIP = NO; 480 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 481 | ENABLE_NS_ASSERTIONS = NO; 482 | ENABLE_STRICT_OBJC_MSGSEND = YES; 483 | GCC_C_LANGUAGE_STANDARD = gnu99; 484 | GCC_NO_COMMON_BLOCKS = YES; 485 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 486 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 487 | GCC_WARN_UNDECLARED_SELECTOR = YES; 488 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 489 | GCC_WARN_UNUSED_FUNCTION = YES; 490 | GCC_WARN_UNUSED_VARIABLE = YES; 491 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 492 | MTL_ENABLE_DEBUG_INFO = NO; 493 | SDKROOT = iphoneos; 494 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 495 | TARGETED_DEVICE_FAMILY = "1,2"; 496 | VALIDATE_PRODUCT = YES; 497 | }; 498 | name = Release; 499 | }; 500 | 97C147061CF9000F007C117D /* Debug */ = { 501 | isa = XCBuildConfiguration; 502 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | CLANG_ENABLE_MODULES = YES; 506 | CODE_SIGN_IDENTITY = "Apple Development"; 507 | CODE_SIGN_STYLE = Automatic; 508 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 509 | DEVELOPMENT_TEAM = U5K74N99H5; 510 | ENABLE_BITCODE = NO; 511 | FRAMEWORK_SEARCH_PATHS = ( 512 | "$(inherited)", 513 | "$(PROJECT_DIR)/Flutter", 514 | ); 515 | INFOPLIST_FILE = Runner/Info.plist; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 517 | LIBRARY_SEARCH_PATHS = ( 518 | "$(inherited)", 519 | "$(PROJECT_DIR)/Flutter", 520 | ); 521 | PRODUCT_BUNDLE_IDENTIFIER = com.dormmom.test; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | PROVISIONING_PROFILE_SPECIFIER = ""; 524 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 525 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 526 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 527 | SWIFT_VERSION = 4.0; 528 | VERSIONING_SYSTEM = "apple-generic"; 529 | }; 530 | name = Debug; 531 | }; 532 | 97C147071CF9000F007C117D /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | CLANG_ENABLE_MODULES = YES; 538 | CODE_SIGN_IDENTITY = "Apple Development"; 539 | CODE_SIGN_STYLE = Automatic; 540 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 541 | DEVELOPMENT_TEAM = U5K74N99H5; 542 | ENABLE_BITCODE = NO; 543 | FRAMEWORK_SEARCH_PATHS = ( 544 | "$(inherited)", 545 | "$(PROJECT_DIR)/Flutter", 546 | ); 547 | INFOPLIST_FILE = Runner/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 549 | LIBRARY_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "$(PROJECT_DIR)/Flutter", 552 | ); 553 | PRODUCT_BUNDLE_IDENTIFIER = com.dormmom.test; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | PROVISIONING_PROFILE_SPECIFIER = ""; 556 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 557 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 558 | SWIFT_VERSION = 4.0; 559 | VERSIONING_SYSTEM = "apple-generic"; 560 | }; 561 | name = Release; 562 | }; 563 | /* End XCBuildConfiguration section */ 564 | 565 | /* Begin XCConfigurationList section */ 566 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 567 | isa = XCConfigurationList; 568 | buildConfigurations = ( 569 | 97C147031CF9000F007C117D /* Debug */, 570 | 97C147041CF9000F007C117D /* Release */, 571 | 249021D3217E4FDB00AE95B9 /* Profile */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 577 | isa = XCConfigurationList; 578 | buildConfigurations = ( 579 | 97C147061CF9000F007C117D /* Debug */, 580 | 97C147071CF9000F007C117D /* Release */, 581 | 249021D4217E4FDB00AE95B9 /* Profile */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | /* End XCConfigurationList section */ 587 | }; 588 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 589 | } 590 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_dialpad_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSMicrophoneUsageDescription 26 | $(EXECUTABLE_NAME) would like to access your microphone for voip calls 27 | UIBackgroundModes 28 | 29 | audio 30 | voip 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | CADisableMinimumFrameDurationOnPhone 52 | 53 | UIApplicationSupportsIndirectInputEvents 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dialpad_example/screens/dialpad_selection.dart'; 3 | 4 | void main() { 5 | WidgetsFlutterBinding.ensureInitialized(); 6 | runApp(MyApp()); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | theme: ThemeData( 14 | iconTheme: IconThemeData(size: 36), 15 | textButtonTheme: TextButtonThemeData( 16 | style: ButtonStyle( 17 | textStyle: MaterialStateProperty.all(TextStyle(fontSize: 20)), 18 | ), 19 | ), 20 | ), 21 | debugShowCheckedModeBanner: false, 22 | home: DialPadSelection(), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/lib/screens/dialpad_basic.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dialpad/flutter_dialpad.dart'; 3 | import 'package:flutter_dialpad_example/widgets/ui_app_bar.dart'; 4 | 5 | class DialPadBasic extends StatelessWidget { 6 | const DialPadBasic({super.key}); 7 | 8 | void _makeCall(String number) { 9 | print('calling $number'); 10 | } 11 | 12 | void _keyPressed(String number) { 13 | print(number); 14 | } 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: UIAppBar(title: 'DialPad Basic-style'), 20 | body: DialPad( 21 | makeCall: _makeCall, 22 | keyPressed: _keyPressed, 23 | onTextChanged: (text) => print(text), 24 | enableDtmf: true, 25 | ), 26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /example/lib/screens/dialpad_ios.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_dialpad/flutter_dialpad.dart'; 4 | 5 | import '../widgets/ui_app_bar.dart'; 6 | 7 | class DialPadIos extends StatelessWidget { 8 | const DialPadIos({super.key}); 9 | 10 | void _makeCall(String number) { 11 | print('calling $number'); 12 | } 13 | 14 | void _keyPressed(String number) { 15 | print(number); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: UIAppBar(title: 'DialPad iOS-style'), 22 | body: DialPad.ios( 23 | makeCall: _makeCall, 24 | keyPressed: _keyPressed, 25 | enableDtmf: true, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/lib/screens/dialpad_metro.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_dialpad/flutter_dialpad.dart'; 4 | import '../widgets/ui_app_bar.dart'; 5 | 6 | class DialPadMetro extends StatelessWidget { 7 | const DialPadMetro({super.key}); 8 | 9 | void _makeCall(String number) { 10 | print('calling $number'); 11 | } 12 | 13 | void _keyPressed(String number) { 14 | print(number); 15 | } 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: UIAppBar(title: 'DialPad Metro-style'), 21 | body: DialPad.metro( 22 | makeCall: _makeCall, 23 | keyPressed: _keyPressed, 24 | enableDtmf: true, 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/lib/screens/dialpad_selection.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'dialpad_ios.dart'; 4 | import 'dialpad_metro.dart'; 5 | import 'dialpad_basic.dart'; 6 | 7 | class DialPadSelection extends StatelessWidget { 8 | const DialPadSelection({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | body: SafeArea( 14 | child: Column( 15 | children: [ 16 | Text("Flutter Dialpad", style: Theme.of(context).textTheme.headlineMedium), 17 | SizedBox(height: 4), 18 | Text("View some of the designs below, click on a style", style: Theme.of(context).textTheme.bodyMedium), 19 | Divider(), 20 | SizedBox(height: 24), 21 | 22 | /// iOS-Style 23 | TextButton.icon( 24 | icon: Icon(Icons.apple), 25 | label: Text("iOS-style"), 26 | onPressed: () { 27 | Navigator.push(context, MaterialPageRoute(builder: (context) => DialPadIos())); 28 | }, 29 | ), 30 | SizedBox(height: 24), 31 | 32 | /// Metro-Style 33 | TextButton.icon( 34 | icon: Icon(Icons.window), 35 | label: Text("Metro-style"), 36 | onPressed: () { 37 | Navigator.push(context, MaterialPageRoute(builder: (context) => DialPadMetro())); 38 | }, 39 | ), 40 | SizedBox(height: 24), 41 | 42 | /// Plain-Style 43 | TextButton( 44 | child: Text("Basic-style"), 45 | onPressed: () { 46 | Navigator.push(context, MaterialPageRoute(builder: (context) => DialPadBasic())); 47 | }, 48 | ), 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /example/lib/widgets/ui_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class UIAppBar extends StatelessWidget implements PreferredSizeWidget { 4 | final String title; 5 | 6 | const UIAppBar({super.key, required this.title}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return AppBar( 11 | title: Text(title), 12 | leading: IconButton( 13 | icon: Icon(Icons.arrow_back), 14 | onPressed: () => Navigator.pop(context), 15 | ), 16 | ); 17 | } 18 | 19 | @override 20 | Size get preferredSize => Size.fromHeight(kToolbarHeight); 21 | } 22 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.18.0" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.5" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.1" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_dialpad: 66 | dependency: "direct dev" 67 | description: 68 | path: ".." 69 | relative: true 70 | source: path 71 | version: "1.1.0" 72 | flutter_dtmf: 73 | dependency: transitive 74 | description: 75 | name: flutter_dtmf 76 | sha256: e95ed7262375d44c21de53b6ee01dd42fd470683bc224cafe82c4c9806df5787 77 | url: "https://pub.dev" 78 | source: hosted 79 | version: "3.0.0" 80 | flutter_masked_text2: 81 | dependency: transitive 82 | description: 83 | path: "." 84 | ref: "156f63b77d23148d9f1544230e3201ab6172f470" 85 | resolved-ref: "156f63b77d23148d9f1544230e3201ab6172f470" 86 | url: "https://github.com/cybex-dev/flutter_masked_text" 87 | source: git 88 | version: "0.9.1" 89 | flutter_test: 90 | dependency: "direct dev" 91 | description: flutter 92 | source: sdk 93 | version: "0.0.0" 94 | flutter_web_plugins: 95 | dependency: transitive 96 | description: flutter 97 | source: sdk 98 | version: "0.0.0" 99 | matcher: 100 | dependency: transitive 101 | description: 102 | name: matcher 103 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 104 | url: "https://pub.dev" 105 | source: hosted 106 | version: "0.12.16" 107 | material_color_utilities: 108 | dependency: transitive 109 | description: 110 | name: material_color_utilities 111 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 112 | url: "https://pub.dev" 113 | source: hosted 114 | version: "0.5.0" 115 | meta: 116 | dependency: transitive 117 | description: 118 | name: meta 119 | sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e 120 | url: "https://pub.dev" 121 | source: hosted 122 | version: "1.10.0" 123 | path: 124 | dependency: transitive 125 | description: 126 | name: path 127 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "1.8.3" 131 | plugin_platform_interface: 132 | dependency: transitive 133 | description: 134 | name: plugin_platform_interface 135 | sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "2.1.7" 139 | sky_engine: 140 | dependency: transitive 141 | description: flutter 142 | source: sdk 143 | version: "0.0.99" 144 | source_span: 145 | dependency: transitive 146 | description: 147 | name: source_span 148 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 149 | url: "https://pub.dev" 150 | source: hosted 151 | version: "1.10.0" 152 | stack_trace: 153 | dependency: transitive 154 | description: 155 | name: stack_trace 156 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 157 | url: "https://pub.dev" 158 | source: hosted 159 | version: "1.11.1" 160 | stream_channel: 161 | dependency: transitive 162 | description: 163 | name: stream_channel 164 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 165 | url: "https://pub.dev" 166 | source: hosted 167 | version: "2.1.2" 168 | string_scanner: 169 | dependency: transitive 170 | description: 171 | name: string_scanner 172 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 173 | url: "https://pub.dev" 174 | source: hosted 175 | version: "1.2.0" 176 | term_glyph: 177 | dependency: transitive 178 | description: 179 | name: term_glyph 180 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 181 | url: "https://pub.dev" 182 | source: hosted 183 | version: "1.2.1" 184 | test_api: 185 | dependency: transitive 186 | description: 187 | name: test_api 188 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 189 | url: "https://pub.dev" 190 | source: hosted 191 | version: "0.6.1" 192 | vector_math: 193 | dependency: transitive 194 | description: 195 | name: vector_math 196 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 197 | url: "https://pub.dev" 198 | source: hosted 199 | version: "2.1.4" 200 | web: 201 | dependency: transitive 202 | description: 203 | name: web 204 | sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 205 | url: "https://pub.dev" 206 | source: hosted 207 | version: "0.3.0" 208 | sdks: 209 | dart: ">=3.2.0-194.0.dev <4.0.0" 210 | flutter: ">=3.3.0" 211 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_dialpad_example 2 | description: Demonstrates how to use the flutter_dialpad plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.17.0 <4.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^1.0.5 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter_dialpad: 21 | path: ../ 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | 34 | # To add assets to your application, add an assets section, like this: 35 | # assets: 36 | # - images/a_dot_burr.jpeg 37 | # - images/a_dot_ham.jpeg 38 | 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.dev/assets-and-images/#resolution-aware. 41 | 42 | # For details regarding adding assets from package dependencies, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | 45 | # To add custom fonts to your application, add a fonts section here, 46 | # in this "flutter" section. Each entry in this list should have a 47 | # "family" key with the font family name, and a "fonts" key with a 48 | # list giving the asset and other descriptors for the font. For 49 | # example: 50 | # fonts: 51 | # - family: Schyler 52 | # fonts: 53 | # - asset: fonts/Schyler-Regular.ttf 54 | # - asset: fonts/Schyler-Italic.ttf 55 | # style: italic 56 | # - family: Trajan Pro 57 | # fonts: 58 | # - asset: fonts/TrajanPro.ttf 59 | # - asset: fonts/TrajanPro_Bold.ttf 60 | # weight: 700 61 | # 62 | # For details regarding fonts from package dependencies, 63 | # see https://flutter.dev/custom-fonts/#from-packages 64 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_dialpad/flutter_dialpad.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | import 'package:flutter_dialpad_example/main.dart'; 12 | 13 | 14 | void main() { 15 | testWidgets('Verify DialPad Added', (WidgetTester tester) async { 16 | // Build our app and trigger a frame. 17 | await tester.pumpWidget(MyApp()); 18 | 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => widget is DialPad, 22 | ), 23 | findsOneWidget, 24 | ); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /lib/flutter_dialpad.dart: -------------------------------------------------------------------------------- 1 | library flutter_dialpad; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_masked_text2/flutter_masked_text2.dart'; 5 | import 'package:flutter_dtmf/dtmf.dart'; 6 | 7 | import 'src/flutter_dialpad.dart'; 8 | 9 | export 'src/flutter_dialpad.dart'; 10 | 11 | typedef DialPadButtonBuilder = Widget Function(BuildContext context, int index, KeyValue key, KeyValue? altKey, String? hint); 12 | 13 | class DialPad extends StatefulWidget { 14 | /// Callback when the dial button is pressed. 15 | final ValueSetter? makeCall; 16 | 17 | /// Initial unformatted text to display in the text field e.g. 15551234567 18 | final String? initialText; 19 | 20 | /// Updated number to display in the text field e.g. 15551234567 21 | final String? withNumber; 22 | 23 | /// Callback when a key is pressed. 24 | final ValueSetter? keyPressed; 25 | 26 | /// Callback when text controller's content changed. 27 | final ValueChanged? onTextChanged; 28 | 29 | /// Whether to hide the dial button. Defaults to false. 30 | final bool hideDialButton; 31 | 32 | /// Whether to hide the backspace button. Defaults to false. 33 | final bool hideBackspaceButton; 34 | 35 | /// Whether to hide the subtitle on the dial pad buttons. Defaults to [false]. 36 | final bool hideSubtitle; 37 | 38 | /// buttonColor is the color of the button on the dial pad. defaults to [Colors.grey] 39 | final Color buttonColor; 40 | 41 | /// Color of the button text, defaults to [Colors.black] 42 | final Color buttonTextColor; 43 | 44 | /// Color of the dial button, defaults to [Colors.green] 45 | final Color dialButtonColor; 46 | 47 | /// Color of the dial button icon, defaults to [Colors.white] 48 | final Color dialButtonIconColor; 49 | 50 | /// Icon for the dial button, defaults to [Icons.phone] 51 | final IconData dialButtonIcon; 52 | 53 | /// Color of the backspace button icon, defaults to [Colors.grey] 54 | final Color backspaceButtonIconColor; 55 | 56 | /// Color of the output text, defaults to [Colors.black] 57 | final Color dialOutputTextColor; 58 | 59 | /// Font size for the output text, defaults to 50 60 | /// Text scales with the screen size using the shortest between (height & width) * 0.001 multiplied by the [dialOutputTextSize] value 61 | final double dialOutputTextSize; 62 | 63 | /// Font size for the output text, defaults to 75 64 | final double buttonTextSize; 65 | 66 | /// Font size for the output text, defaults to 25 67 | /// Text scales with the screen size using the shortest between (height & width) * 0.001 multiplied by the [subtitleTextSize] value 68 | final double subtitleTextSize; 69 | 70 | /// outputMask is the mask applied to the output text. Defaults to (000) 000-0000 71 | final String? outputMask; 72 | final String hint; 73 | 74 | /// Whether to enable DTMF tones. Defaults to [false]. 75 | /// Disabled temporarily until [flutter_dtmf] has been updated. 76 | final bool enableDtmf; 77 | 78 | /// Builder for the keypad buttons. Defaults to [DialPadButtonBuilder]. 79 | /// Note: this has not yet been fully integrated for customer use - this will be available in a future release. 80 | final DialPadButtonBuilder? keypadButtonBuilder; 81 | 82 | /// Generator for the keypad buttons. Defaults to [PhoneKeypadGenerator]. 83 | final KeypadIndexedGenerator? generator; 84 | 85 | /// Button display style (clipping). Defaults to [ButtonType.rectangle]. 86 | final ButtonType buttonType; 87 | 88 | /// Padding around the button. Defaults to [EdgeInsets.all(0)]. 89 | final EdgeInsets buttonPadding; 90 | 91 | /// Padding around the button. Defaults to [buttonPadding]. 92 | final EdgeInsets? backspaceButtonPadding; 93 | 94 | /// Background color of the backspace button. Defaults to [buttonColor]. 95 | final Color? backspaceButtonColor; 96 | 97 | /// Icon size of the backspace button. Defaults to [50]. 98 | final double? backspaceButtonIconSize; 99 | 100 | /// Padding around the button. Defaults to [buttonPadding]. 101 | final EdgeInsets? dialButtonPadding; 102 | 103 | /// Whether to call [makeCall] when the enter key is pressed. Defaults to false. 104 | final bool callOnEnter; 105 | 106 | /// Whether to copy the text to the clipboard when the text field is tapped. Defaults to true. 107 | final bool copyToClipboard; 108 | 109 | /// Whether to paste the text from the clipboard when the text field is tapped. Defaults to true. 110 | final bool pasteFromClipboard; 111 | 112 | /// Padding around the text field. Defaults to [EdgeInsets.only(left: 16, right: 16, top: 20, bottom: 16)]. 113 | final EdgeInsets textFieldPadding; 114 | 115 | /// Scaling type for the dial pad. Defaults to [ScalingType.min]. 116 | final ScalingType scalingType; 117 | 118 | /// Scaling size for the dial pad. Defaults to [ScalingSize.medium]. 119 | final ScalingSize scalingSize; 120 | 121 | /// [ScalingType] for the dial button. Defaults to [ScalingSize.medium]. 122 | final ScalingSize? dialingButtonScalingSize; 123 | 124 | /// [ScalingType] for the dial button. Defaults to [ScalingSize.small]. 125 | final ScalingSize? backspaceButtonScalingSize; 126 | 127 | /// Clamp the scaling size to a maximum value, this limits the rescaling size as a percentage of the provided value e.g. font or text size. Defaults to 1.0 128 | final double maxScalingSize; 129 | 130 | /// Clamp the scaling size to a minimum value, this limits the rescaling size as a percentage of the provided value e.g. font or text size. Defaults to 0.2 131 | final double minScalingSize; 132 | 133 | /// Add dial button icon size. Defaults to [75]. 134 | final double? dialButtonIconSize; 135 | 136 | /// Add dial button content padding. Defaults to [EdgeInsets.zero]. 137 | final EdgeInsets? dialContentPadding; 138 | 139 | /// Add backspace button content padding. Defaults to [EdgeInsets.zero]. 140 | final EdgeInsets? backspaceContentPadding; 141 | 142 | /// Add keypad button content padding used by [_defaultKeypadButtonBuilder]. Defaults to [EdgeInsets.zero]. 143 | final EdgeInsets? keyButtonContentPadding; 144 | 145 | /// Hide backspace button when text field is empty. Defaults to [false]. 146 | final bool hideBackspaceOnEmpty; 147 | 148 | DialPad({ 149 | this.makeCall, 150 | this.initialText, 151 | this.withNumber, 152 | this.keyPressed, 153 | this.onTextChanged, 154 | this.hideDialButton = false, 155 | this.hideBackspaceButton = false, 156 | this.hideSubtitle = false, 157 | this.outputMask = '(000) 000-0000', 158 | this.hint = '(000) 000-0000', 159 | this.buttonColor = Colors.grey, 160 | this.buttonTextColor = Colors.black, 161 | this.dialButtonColor = Colors.green, 162 | this.dialButtonIconColor = Colors.white, 163 | this.dialButtonIcon = Icons.phone, 164 | this.dialOutputTextColor = Colors.black, 165 | this.dialOutputTextSize = 50, 166 | this.buttonTextSize = 75, 167 | this.subtitleTextSize = 25, 168 | this.backspaceButtonIconColor = Colors.grey, 169 | this.enableDtmf = false, 170 | @Deprecated('This has not yet been fully integrated for customer use and thus has no effect on the output - will be available in a future release.') 171 | this.keypadButtonBuilder, 172 | this.generator = const PhoneKeypadGenerator(), 173 | this.buttonType = ButtonType.rectangle, 174 | this.buttonPadding = const EdgeInsets.all(0), 175 | this.backspaceButtonPadding = const EdgeInsets.all(0), 176 | this.dialButtonPadding = const EdgeInsets.all(0), 177 | this.callOnEnter = false, 178 | this.copyToClipboard = true, 179 | this.pasteFromClipboard = true, 180 | this.textFieldPadding = const EdgeInsets.only(left: 16, right: 16, top: 20, bottom: 16), 181 | this.scalingType = ScalingType.min, 182 | this.scalingSize = ScalingSize.medium, 183 | this.dialingButtonScalingSize, 184 | this.backspaceButtonScalingSize, 185 | this.backspaceButtonColor, 186 | this.backspaceButtonIconSize, 187 | this.minScalingSize = 0.2, 188 | this.maxScalingSize = 1.0, 189 | this.dialButtonIconSize, 190 | this.dialContentPadding, 191 | this.backspaceContentPadding, 192 | this.keyButtonContentPadding, 193 | this.hideBackspaceOnEmpty = false, 194 | }); 195 | 196 | /// Returns a [DialPad] with an iOS-style design (i.e. Apple). 197 | factory DialPad.ios({ 198 | ValueSetter? makeCall, 199 | ValueSetter? keyPressed, 200 | bool enableDtmf = false, 201 | }) { 202 | return DialPad( 203 | makeCall: makeCall, 204 | keyPressed: keyPressed, 205 | enableDtmf: enableDtmf, 206 | // Cupertino icons should be used here 207 | dialButtonIcon: Icons.phone, 208 | backspaceButtonIconColor: Colors.grey, 209 | generator: IosKeypadGenerator(), 210 | dialOutputTextColor: Colors.black87, 211 | buttonTextColor: Colors.black87, 212 | buttonColor: Colors.grey[300]!, 213 | buttonType: ButtonType.circle, 214 | backspaceButtonIconSize: 50, 215 | dialButtonIconSize: 75, 216 | buttonTextSize: 75, 217 | subtitleTextSize: 25, 218 | scalingSize: ScalingSize.small, 219 | dialingButtonScalingSize: ScalingSize.medium, 220 | backspaceButtonScalingSize: ScalingSize.medium, 221 | buttonPadding: EdgeInsets.all(8), 222 | backspaceButtonPadding: EdgeInsets.all(12), 223 | dialButtonPadding: EdgeInsets.all(8), 224 | maxScalingSize: 0.7, 225 | hideBackspaceOnEmpty: true, 226 | ); 227 | } 228 | 229 | /// Creates a dial pad with a metro-style (i.e. Windows). 230 | factory DialPad.metro({ 231 | ValueSetter? makeCall, 232 | ValueSetter? keyPressed, 233 | bool enableDtmf = false, 234 | }) { 235 | return DialPad( 236 | makeCall: makeCall, 237 | keyPressed: keyPressed, 238 | enableDtmf: enableDtmf, 239 | // metro icons should be used here 240 | dialButtonIcon: Icons.phone, 241 | backspaceButtonIconColor: Colors.grey, 242 | generator: PhoneKeypadGenerator(), 243 | dialOutputTextColor: Colors.black87, 244 | buttonTextColor: Colors.black54, 245 | buttonColor: Colors.white, 246 | buttonType: ButtonType.rectangle, 247 | dialButtonColor: Colors.blue, 248 | scalingSize: ScalingSize.medium, 249 | ); 250 | } 251 | 252 | @override 253 | State createState() => _DialPadState(); 254 | } 255 | 256 | class _DialPadState extends State { 257 | late final MaskedTextController _controller; 258 | String _value = ""; 259 | 260 | @override 261 | void initState() { 262 | super.initState(); 263 | _controller = MaskedTextController(text: widget.initialText ?? widget.withNumber, mask: widget.outputMask); 264 | _value = _controller.text; 265 | } 266 | 267 | /// Handles text field content change, notifies [onTextChanged] callback 268 | void _onTextChanged() { 269 | if (widget.onTextChanged != null) { 270 | widget.onTextChanged!(_controller.text); 271 | } 272 | } 273 | 274 | /// Handles keypad button press, this includes numbers and [DialActionKey] except [DialActionKey.backspace] 275 | void _onKeyPressed(String? value) { 276 | if (value != null) { 277 | setState(() { 278 | _value += value; 279 | _controller.updateText(_value); 280 | }); 281 | } 282 | } 283 | 284 | /// Handles backspace button press 285 | void _onBackspacePressed() { 286 | if (_value.isEmpty) { 287 | return; 288 | } 289 | 290 | setState(() { 291 | _value = _value.substring(0, _value.length - 1); 292 | _controller.text = _value; 293 | }); 294 | } 295 | 296 | /// Handles dial button press 297 | void _onDialPressed() { 298 | if (widget.makeCall != null && _value.isNotEmpty) { 299 | widget.makeCall!(_value); 300 | } 301 | } 302 | 303 | /// Handles all keyboard / UI keypad button presses 304 | void _onKeypadPressed(KeyValue key) { 305 | if (key is ActionKey && key.action == DialActionKey.backspace) { 306 | // handle backspace 307 | _onBackspacePressed(); 308 | } else if (key is ActionKey && key.action == DialActionKey.enter) { 309 | if (widget.callOnEnter) { 310 | _onDialPressed(); 311 | } 312 | } else { 313 | // For numbers, and all actions except backspace 314 | _onKeyPressed(key.value); 315 | // Play the dtmf tone if enabled 316 | if (widget.enableDtmf) { 317 | Dtmf.playTone(digits: key.value.trim(), samplingRate: 8000, durationMs: 160); 318 | } 319 | } 320 | 321 | // notifies UI of input changed 322 | _onTextChanged(); 323 | } 324 | 325 | Widget _defaultKeypadButtonBuilder(BuildContext context, int index, KeyValue key, KeyValue? altKey, String? hint) { 326 | return ActionButton( 327 | title: key.value, 328 | subtitle: altKey?.value ?? hint, 329 | color: widget.buttonColor, 330 | hideSubtitle: widget.hideSubtitle, 331 | onTap: () => _onKeypadPressed(key), 332 | onLongPressed: () => _onKeypadPressed(altKey ?? key), 333 | buttonType: widget.buttonType, 334 | padding: widget.buttonPadding, 335 | contentPadding: widget.keyButtonContentPadding, 336 | textColor: widget.buttonTextColor, 337 | iconColor: widget.buttonTextColor, 338 | subtitleIconColor: widget.buttonTextColor, 339 | subtitleFontSize: widget.subtitleTextSize, 340 | fontSize: widget.buttonTextSize, 341 | scalingType: widget.scalingType, 342 | scalingSize: widget.scalingSize, 343 | minScalingSize: widget.minScalingSize, 344 | maxScalingSize: widget.maxScalingSize, 345 | ); 346 | } 347 | 348 | @override 349 | void didUpdateWidget(DialPad oldWidget) { 350 | super.didUpdateWidget(oldWidget); 351 | 352 | // if the withNumber property has changed, update the text field 353 | if (widget.withNumber != null && widget.withNumber != oldWidget.withNumber) { 354 | // update text field 355 | _controller.updateText(widget.withNumber ?? ""); 356 | // update value with masked number 357 | _value = _controller.text; 358 | } 359 | } 360 | 361 | @override 362 | Widget build(BuildContext context) { 363 | final _keypadButtonBuilder = /*widget.keypadButtonBuilder ?? */ _defaultKeypadButtonBuilder; 364 | final _generator = widget.generator ?? IosKeypadGenerator(); 365 | 366 | /// Dial button 367 | final dialButton = widget.hideDialButton 368 | ? null 369 | : ActionButton( 370 | iconSize: widget.dialButtonIconSize ?? 75, 371 | padding: widget.dialButtonPadding ?? widget.buttonPadding, 372 | buttonType: widget.buttonType, 373 | icon: widget.dialButtonIcon, 374 | iconColor: widget.dialButtonIconColor, 375 | color: widget.dialButtonColor, 376 | onTap: _onDialPressed, 377 | scalingType: widget.scalingType, 378 | scalingSize: widget.dialingButtonScalingSize ?? widget.scalingSize, 379 | minScalingSize: widget.minScalingSize, 380 | maxScalingSize: widget.maxScalingSize, 381 | contentPadding: widget.dialContentPadding, 382 | // NOTE(cybex-dev) add as option in future 383 | // disabled: _value.isEmpty || widget.makeCall == null, 384 | ); 385 | 386 | /// Backspace button 387 | final backspaceButton = widget.hideBackspaceButton || (_value.isEmpty && widget.hideBackspaceOnEmpty) 388 | ? null 389 | : ActionButton( 390 | onTap: () => _onKeypadPressed(ActionKey.backspace()), 391 | // disabled: _value.isEmpty, 392 | buttonType: widget.buttonType, 393 | iconSize: widget.backspaceButtonIconSize ?? 50, 394 | iconColor: widget.backspaceButtonIconColor, 395 | padding: widget.backspaceButtonPadding ?? widget.buttonPadding, 396 | icon: Icons.backspace, 397 | color: widget.backspaceButtonColor ?? Colors.transparent, 398 | scalingType: widget.scalingType, 399 | scalingSize: widget.backspaceButtonScalingSize ?? widget.scalingSize, 400 | minScalingSize: widget.minScalingSize, 401 | maxScalingSize: widget.maxScalingSize, 402 | contentPadding: widget.backspaceContentPadding, 403 | ); 404 | 405 | /// Footer contains the dial and backspace buttons 406 | final footer = dialButton == null && backspaceButton == null 407 | ? null 408 | : Row( 409 | children: [ 410 | Expanded(child: Container()), 411 | Expanded(child: dialButton ?? Container()), 412 | Expanded(child: backspaceButton ?? Container()), 413 | ], 414 | ); 415 | 416 | final children = [ 417 | Padding( 418 | padding: widget.textFieldPadding, 419 | child: PhoneTextField( 420 | textColor: widget.dialOutputTextColor, 421 | textSize: widget.dialOutputTextSize, 422 | decoration: InputDecoration(border: InputBorder.none, hintText: widget.hint), 423 | controller: _controller, 424 | copyToClipboard: widget.copyToClipboard, 425 | readOnly: !widget.pasteFromClipboard, 426 | scalingType: widget.scalingType, 427 | minScalingSize: widget.minScalingSize, 428 | maxScalingSize: widget.maxScalingSize, 429 | ), 430 | ), 431 | Expanded( 432 | child: KeypadGrid( 433 | itemCount: 12, 434 | itemBuilder: (context, index) { 435 | final key = _generator.get(index); 436 | final altKey = _generator.getAlt(index); 437 | final hint = _generator.hint(index); 438 | return _keypadButtonBuilder(context, index, key, altKey, hint); 439 | }, 440 | footer: footer, 441 | ), 442 | ) 443 | ]; 444 | 445 | return KeypadFocusNode( 446 | onKeypadPressed: _onKeypadPressed, 447 | child: Column( 448 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 449 | mainAxisSize: MainAxisSize.min, 450 | children: children, 451 | ), 452 | ); 453 | } 454 | 455 | @override 456 | void dispose() { 457 | _controller.dispose(); 458 | super.dispose(); 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /lib/src/flutter_dialpad.dart: -------------------------------------------------------------------------------- 1 | export 'generator/generator.dart'; 2 | export 'models/models.dart'; 3 | export 'widgets/widgets.dart'; -------------------------------------------------------------------------------- /lib/src/generator/generator.dart: -------------------------------------------------------------------------------- 1 | export 'ios_keypad_generator.dart'; 2 | export 'keypad_indexed_generator.dart'; 3 | export 'phone_keypad_generator.dart'; -------------------------------------------------------------------------------- /lib/src/generator/ios_keypad_generator.dart: -------------------------------------------------------------------------------- 1 | import '../models/key_value.dart'; 2 | import 'keypad_indexed_generator.dart'; 3 | 4 | /// Phone keypad generator for a standard mobile phone keypad, including the digits 0-9, asterisk, and hash in a logical grid format from indexed values (0-11): 5 | /// ``` 6 | /// 1 2 3 7 | /// 4 5 6 8 | /// 7 8 9 9 | /// * 0 # 10 | /// ``` 11 | /// 12 | /// For index values 0-8, 10, the [DigitKey] value is returned. For index values 9 and 11, the [ActionKey] value is returned. 13 | class IosKeypadGenerator extends KeypadIndexedGenerator { 14 | const IosKeypadGenerator(); 15 | 16 | @override 17 | KeyValue get(int index) { 18 | switch (index) { 19 | case 9: 20 | return ActionKey.asterisk(); 21 | case 10: 22 | return DigitKey.value('0'); 23 | case 11: 24 | return ActionKey.hash(); 25 | default: 26 | return DigitKey(index + 1); 27 | } 28 | } 29 | 30 | @override 31 | KeyValue? getAlt(int index) { 32 | switch (index) { 33 | case 10: 34 | return ActionKey.plus(); 35 | default: 36 | return null; 37 | } 38 | } 39 | 40 | /// Returns a hint for a given index. 41 | /// ``` 42 | /// null ABC DEF 43 | /// GHI JKL MNO 44 | /// PQRS TUV WXYZ 45 | /// * null # 46 | /// ``` 47 | /// 48 | @override 49 | String? hint(int index) { 50 | switch (index) { 51 | case 0: 52 | return null; 53 | case 1: 54 | return 'ABC'; 55 | case 2: 56 | return 'DEF'; 57 | case 3: 58 | return 'GHI'; 59 | case 4: 60 | return 'JKL'; 61 | case 5: 62 | return 'MNO'; 63 | case 6: 64 | return 'PQRS'; 65 | case 7: 66 | return 'TUV'; 67 | case 8: 68 | return 'WXYZ'; 69 | case 10: 70 | return '+'; 71 | default: 72 | return null; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/generator/keypad_indexed_generator.dart: -------------------------------------------------------------------------------- 1 | import '../models/key_value.dart'; 2 | 3 | /// A generator that returns a [KeyValue] for a given index. Extensibility allows keypads of different formats, layouts, etc. 4 | abstract class KeypadIndexedGenerator { 5 | const KeypadIndexedGenerator(); 6 | 7 | /// Returns a [KeyValue] for a given index. 8 | KeyValue get(int index); 9 | 10 | /// Returns a [KeyValue] for a given index's alternate selection (i.e. long press). 11 | KeyValue? getAlt(int index); 12 | 13 | /// Returns a hint for a given index. 14 | String? hint(int index) => null; 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/generator/phone_keypad_generator.dart: -------------------------------------------------------------------------------- 1 | import '../models/key_value.dart'; 2 | import 'keypad_indexed_generator.dart'; 3 | 4 | /// Phone keypad generator for a standard mobile phone keypad, including the digits 0-9, asterisk, and hash in a logical grid format from indexed values (0-11): 5 | /// ``` 6 | /// 1 2 3 7 | /// 4 5 6 8 | /// 7 8 9 9 | /// * 0 # 10 | /// ``` 11 | /// 12 | /// For index values 0-8, 10, the [DigitKey] value is returned. For index values 9 and 11, the [ActionKey] value is returned. 13 | class PhoneKeypadGenerator extends KeypadIndexedGenerator { 14 | const PhoneKeypadGenerator(); 15 | 16 | @override 17 | KeyValue get(int index) { 18 | switch (index) { 19 | case 9: 20 | return ActionKey.asterisk(); 21 | case 10: 22 | return DigitKey.value('0'); 23 | case 11: 24 | return ActionKey.hash(); 25 | default: 26 | return DigitKey(index + 1); 27 | } 28 | } 29 | 30 | @override 31 | KeyValue? getAlt(int index) { 32 | switch (index) { 33 | case 9: 34 | return ActionKey.plus(); 35 | default: 36 | return null; 37 | } 38 | } 39 | 40 | /// Returns a hint for a given index. 41 | /// ``` 42 | /// null ABC DEF 43 | /// GHI JKL MNO 44 | /// PQRS TUV WXYZ 45 | /// + null null 46 | /// ``` 47 | /// 48 | @override 49 | String? hint(int index) { 50 | switch (index) { 51 | case 1: 52 | return 'ABC'; 53 | case 2: 54 | return 'DEF'; 55 | case 3: 56 | return 'GHI'; 57 | case 4: 58 | return 'JKL'; 59 | case 5: 60 | return 'MNO'; 61 | case 6: 62 | return 'PQRS'; 63 | case 7: 64 | return 'TUV'; 65 | case 8: 66 | return 'WXYZ'; 67 | case 9: 68 | return '+'; 69 | default: 70 | return null; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/mixins/scalable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import '../widgets/scalable/scaling_size.dart'; 5 | import '../widgets/scalable/scaling_type.dart'; 6 | 7 | mixin Scalable { 8 | /// Rescale a value based on the [ScalingType] provided. 9 | double rescale(Size screenSize, ScalingType scalingType, double value, {ScalingSize scalingSize = ScalingSize.small, double? minClamp, double? maxClamp}) { 10 | assert(value >= 0, "value must be greater than or equal to 0"); 11 | assert(minClamp == null || minClamp >= 0, "minClamp must be greater than or equal to 0"); 12 | assert(maxClamp == null || maxClamp >= 0, "maxClamp must be greater than or equal to 0"); 13 | assert(minClamp == null || maxClamp == null || minClamp <= maxClamp, "minClamp must be less than or equal to maxClamp"); 14 | 15 | double scalar = scalingSize.scalar; 16 | double sizeFactor = 1.0; 17 | 18 | switch (scalingType) { 19 | case ScalingType.fixed: 20 | sizeFactor = 1.0; 21 | break; 22 | case ScalingType.height: 23 | sizeFactor = screenSize.height * scalar; 24 | break; 25 | case ScalingType.width: 26 | sizeFactor = screenSize.width * scalar; 27 | break; 28 | case ScalingType.min: 29 | sizeFactor = min(screenSize.width, screenSize.height) * scalar; 30 | break; 31 | case ScalingType.max: 32 | sizeFactor = max(screenSize.width, screenSize.height) * scalar; 33 | break; 34 | } 35 | 36 | // apply clamps 37 | if (minClamp != null && sizeFactor < minClamp) { 38 | // print("[minClamp] adjusting sizeFactor from $sizeFactor to $minClamp"); 39 | sizeFactor = minClamp; 40 | } else if (maxClamp != null && sizeFactor > maxClamp) { 41 | // print("[maxClamp] adjusting sizeFactor from $sizeFactor to $maxClamp"); 42 | sizeFactor = maxClamp; 43 | } 44 | 45 | return value * sizeFactor; 46 | } 47 | } -------------------------------------------------------------------------------- /lib/src/models/key_value.dart: -------------------------------------------------------------------------------- 1 | import '../generator/keypad_indexed_generator.dart'; 2 | import '../generator/phone_keypad_generator.dart'; 3 | 4 | /// A raw KeyEvent, retaining a keyboard key. 5 | class KeyValue { 6 | final String value; 7 | 8 | /// Returns true if the value is a digit (0-9). 9 | bool get isDigit => int.tryParse(value) != null; 10 | 11 | /// Returns true if the value is a symbol (* or #). 12 | bool get isSymbol => value == '*' || value == '#'; 13 | 14 | const KeyValue(this.value) : assert(value.length <= 1, 'value must be a single character or empty'); 15 | 16 | @override 17 | String toString() => "KeyValue('$value')"; 18 | } 19 | 20 | class DigitKey extends KeyValue { 21 | DigitKey(int value) : super(value.toString()); 22 | 23 | /// Returns a [DigitKey] for a given index using the provided [KeypadIndexedGenerator]. 24 | factory DigitKey.index(int index, {KeypadIndexedGenerator generator = const PhoneKeypadGenerator()}) { 25 | final key = generator.get(index); 26 | 27 | // we will only know what type of key we have at runtime, so we need to assert that it is a DigitKey 28 | assert( 29 | key is DigitKey, 30 | 'generator must return a DigitKey, is your generator configured correctly or check the index value?' 31 | 'index: $index, generator key: $key'); 32 | 33 | return key as DigitKey; 34 | } 35 | 36 | factory DigitKey.value(String value) { 37 | assert(value.length == 1, 'value must be a single character'); 38 | assert(int.tryParse(value) != null, 'value must be a valid numeric character'); 39 | assert(int.parse(value) >= 0 && int.parse(value) <= 9, 'value must conform to the constraints 0 <= value <= 9'); 40 | return DigitKey(int.parse(value)); 41 | } 42 | } 43 | 44 | enum DialActionKey { backspace, asterisk, hash, plus, enter } 45 | 46 | class ActionKey extends KeyValue { 47 | 48 | final DialActionKey action; 49 | 50 | const ActionKey(this.action, super.value); 51 | 52 | const ActionKey.enter() : this(DialActionKey.enter, ''); 53 | 54 | const ActionKey.backspace() : this(DialActionKey.backspace, ''); 55 | 56 | const ActionKey.hash() : this(DialActionKey.hash, '#'); 57 | 58 | const ActionKey.plus() : this(DialActionKey.plus, '+'); 59 | 60 | const ActionKey.asterisk() : this(DialActionKey.asterisk, '*'); 61 | 62 | @override 63 | String toString() => "ActionKey('$value')"; 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'key_value.dart'; -------------------------------------------------------------------------------- /lib/src/utils/material_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | MaterialColor createMaterialColor(Color color) { 4 | List strengths = [.05]; 5 | Map swatch = {}; 6 | final int r = color.red, g = color.green, b = color.blue; 7 | 8 | for (int i = 1; i < 10; i++) { 9 | strengths.add(0.1 * i); 10 | } 11 | for (var strength in strengths) { 12 | final double ds = 0.5 - strength; 13 | swatch[(strength * 1000).round()] = Color.fromRGBO( 14 | r + ((ds < 0 ? r : (255 - r)) * ds).round(), 15 | g + ((ds < 0 ? g : (255 - g)) * ds).round(), 16 | b + ((ds < 0 ? b : (255 - b)) * ds).round(), 17 | 1, 18 | ); 19 | } 20 | return MaterialColor(color.value, swatch); 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/widgets/buttons/action_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../mixins/scalable.dart'; 3 | import '../scalable/scalable.dart'; 4 | 5 | class ActionButton extends StatelessWidget with Scalable { 6 | final Key? key; 7 | 8 | /// Title to display on the button. If [icon] is provided, this will be ignored. 9 | final String? title; 10 | 11 | /// Subtitle (hint) to display below the title. If [subtitleIcon] is provided, this will be ignored. If neither are provided, subtitle (hint) will be hidden. 12 | final String? subtitle; 13 | 14 | /// Whether to hide the subtitle (hint). Defaults to false. 15 | final bool hideSubtitle; 16 | 17 | /// Background color of the button. Defaults to system/material color. 18 | final Color color; 19 | 20 | /// Text color of the button. Defaults to [Colors.black]. 21 | final Color textColor; 22 | 23 | /// Icon to replace the title. 24 | final IconData? icon; 25 | 26 | /// Icon to replace the subtitle (hint). If not provided, subtitle (hint) will be used or hidden if not provided. 27 | final IconData? subtitleIcon; 28 | 29 | /// Color of the title icon. Defaults to [Colors.white]. 30 | final Color iconColor; 31 | 32 | /// Color of the subtitle icon. Defaults to [iconColor]. 33 | final Color? subtitleIconColor; 34 | 35 | /// Callback when the button is tapped. 36 | final VoidCallback? onTap; 37 | 38 | /// Callback when the button is held down for a longer period of time. 39 | final VoidCallback? onLongPressed; 40 | 41 | /// Button display style (clipping). Defaults to [ButtonType.rectangle]. 42 | /// [ButtonType.circle] will clip the button to a circle e.g. an iPhone keypad 43 | /// [ButtonType.rectangle] will clip the button to a rectangle e.g. an Android keypad 44 | final ButtonType buttonType; 45 | 46 | /// Padding around the button. Defaults to [EdgeInsets.all(12)]. 47 | final EdgeInsets padding; 48 | 49 | /// Font size for the title, as a percentage of the screen height. Defaults to 75. 50 | /// 51 | /// For example, if the screen height/width (the smaller of the 2) is 1000, the title font size will be 75. 52 | final double fontSize; 53 | 54 | /// Font size for the subtitle, as a percentage of the screen height. Defaults to 25. 55 | /// 56 | /// For example, if the screen height/width (the smaller of the 2) is 1000, the title font size will be 25. 57 | final double subtitleFontSize; 58 | 59 | /// Icon size (icon which replaces title), as a percentage of the screen height. Defaults to 75. 60 | /// 61 | /// For example, if the screen height/width (the smaller of the 2) is 1000, the icon size will be 75. 62 | final double iconSize; 63 | 64 | /// Icon size (icon which replaces subtitle), as a percentage of the screen height. Defaults to 35. 65 | /// 66 | /// For example, if the screen height/width (the smaller of the 2) is 1000, the icon size will be 35. 67 | final double subtitleIconSize; 68 | 69 | /// Whether to disable the button. Defaults to false. 70 | final bool disabled; 71 | 72 | /// [ScalingType] for the button. Defaults to [ScalingType.fixed]. 73 | final ScalingType scalingType; 74 | 75 | /// [ScalingSize] for the button. Defaults to [ScalingSize.small]. 76 | final ScalingSize scalingSize; 77 | 78 | /// Minimum scaling size for the button content. Defaults to null. 79 | final double? minScalingSize; 80 | 81 | /// Maximum scaling size for the button content. Defaults to null. 82 | final double? maxScalingSize; 83 | 84 | /// Padding around the button's content. Defaults to null. 85 | final EdgeInsets? contentPadding; 86 | 87 | ActionButton({ 88 | this.key, 89 | this.title, 90 | this.subtitle, 91 | this.hideSubtitle = false, 92 | this.color = Colors.grey, 93 | this.textColor = Colors.black, 94 | this.icon, 95 | this.subtitleIcon, 96 | this.iconColor = Colors.white, 97 | this.subtitleIconColor, 98 | this.onTap, 99 | this.onLongPressed, 100 | this.buttonType = ButtonType.rectangle, 101 | this.padding = const EdgeInsets.all(0), 102 | this.fontSize = 75, 103 | this.subtitleFontSize = 25, 104 | this.iconSize = 75, 105 | this.subtitleIconSize = 35, 106 | this.disabled = false, 107 | this.scalingType = ScalingType.fixed, 108 | this.scalingSize = ScalingSize.small, 109 | this.minScalingSize, 110 | this.maxScalingSize, 111 | this.contentPadding, 112 | }); 113 | 114 | /// Get title widget, prefer icon over title 115 | Widget _buildTitleWidget(Size screenSize) { 116 | double size = rescale(screenSize, scalingType, icon != null ? iconSize : fontSize, scalingSize: scalingSize, minClamp: minScalingSize, maxClamp: maxScalingSize); 117 | Widget widget = icon != null 118 | ? Icon(icon, size: size, color: iconColor) 119 | : Text( 120 | title!, 121 | style: TextStyle( 122 | fontSize: size, 123 | color: textColor, 124 | ), 125 | ); 126 | 127 | // correction for asterisk being "higher" than other buttons (only if we don't have subtitles to show) 128 | bool hasSubtitle = (subtitleIcon == null && subtitle == null); 129 | bool showSubtitle = hasSubtitle && !hideSubtitle; 130 | if (title == "*" && !showSubtitle) { 131 | widget = Transform.translate( 132 | offset: Offset(0, 10 * size), 133 | child: widget, 134 | ); 135 | } 136 | return widget; 137 | } 138 | 139 | /// Get subtitle widget, prefer subtitleIcon over subtitle 140 | Widget? _buildSubtitleWidget(Size screenSize) { 141 | return subtitleIcon != null 142 | ? Icon(subtitleIcon, size: rescale(screenSize, scalingType, subtitleIconSize, scalingSize: scalingSize, minClamp: minScalingSize, maxClamp: maxScalingSize), color: subtitleIconColor ?? iconColor) 143 | : (subtitle != null) 144 | ? Text( 145 | subtitle ?? "", 146 | style: TextStyle(color: textColor, fontSize: rescale(screenSize, scalingType, subtitleFontSize, scalingSize: scalingSize, minClamp: minScalingSize, maxClamp: maxScalingSize)), 147 | ) 148 | : null; 149 | } 150 | 151 | @override 152 | Widget build(BuildContext context) { 153 | return LayoutBuilder(builder: (context, constraints) { 154 | Size size = constraints.biggest; 155 | 156 | // Get title widget, prefer icon over title 157 | Widget titleWidget = _buildTitleWidget(size); 158 | 159 | // Get subtitle widget, prefer subtitleIcon over subtitle 160 | final subtitleWidget = _buildSubtitleWidget(size); 161 | 162 | // Create dial button text content 163 | final child = subtitleWidget == null || hideSubtitle == true 164 | ? titleWidget 165 | : Column( 166 | mainAxisAlignment: MainAxisAlignment.center, 167 | mainAxisSize: MainAxisSize.min, 168 | children: [ 169 | titleWidget, 170 | subtitleWidget, 171 | ], 172 | ); 173 | 174 | return Padding( 175 | padding: padding, 176 | child: ScalableButton( 177 | color: color, 178 | buttonType: buttonType, 179 | disabled: disabled, 180 | child: child, 181 | onPressed: onTap != null ? onTap : null, 182 | padding: contentPadding ?? EdgeInsets.all(0), 183 | onLongPressed: onLongPressed != null ? onLongPressed : null, 184 | ), 185 | ); 186 | }); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/src/widgets/buttons/buttons.dart: -------------------------------------------------------------------------------- 1 | export 'action_button.dart'; -------------------------------------------------------------------------------- /lib/src/widgets/keypad_focus_node.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import '../models/key_value.dart'; 5 | 6 | /// This widget is used to capture key events from the keyboard, and translate them into [DigitKey] or [ActionKey] events. 7 | class KeypadFocusNode extends StatelessWidget { 8 | /// This callback is called when a key is pressed on the keyboard. 9 | final ValueChanged onKeypadPressed; 10 | 11 | /// The child widget that will be focused. 12 | final Widget child; 13 | 14 | const KeypadFocusNode({super.key, required this.onKeypadPressed, required this.child}); 15 | 16 | /// Handles the key events from the keyboard, and translates them into [DigitKey] or [ActionKey] events that are passed to [onKeypadPressed]. 17 | /// Returns [KeyEventResult.handled] if the event was handled, or [KeyEventResult.ignored] if the event was ignored. 18 | KeyEventResult _handleOnKeyEvent(FocusNode node, KeyEvent event) { 19 | if ((event is KeyRepeatEvent || event is KeyDownEvent) && event.logicalKey == LogicalKeyboardKey.backspace) { 20 | onKeypadPressed(ActionKey.backspace()); 21 | return KeyEventResult.handled; 22 | } else if (event is KeyDownEvent) { 23 | // check if this is a key down event, otherwise we might get the same event multiple times 24 | final physicalKey = event.physicalKey; 25 | final logicalKey = event.logicalKey; 26 | if (physicalKey == PhysicalKeyboardKey.numpad0 || physicalKey == PhysicalKeyboardKey.digit0) { 27 | onKeypadPressed(DigitKey(0)); 28 | } else if (physicalKey == PhysicalKeyboardKey.numpad1 || physicalKey == PhysicalKeyboardKey.digit1) { 29 | onKeypadPressed(DigitKey(1)); 30 | } else if (physicalKey == PhysicalKeyboardKey.numpad2 || physicalKey == PhysicalKeyboardKey.digit2) { 31 | onKeypadPressed(DigitKey(2)); 32 | } else if (physicalKey == PhysicalKeyboardKey.numpad3 || physicalKey == PhysicalKeyboardKey.digit3) { 33 | onKeypadPressed(DigitKey(3)); 34 | } else if (physicalKey == PhysicalKeyboardKey.numpad4 || physicalKey == PhysicalKeyboardKey.digit4) { 35 | onKeypadPressed(DigitKey(4)); 36 | } else if (physicalKey == PhysicalKeyboardKey.numpad5 || physicalKey == PhysicalKeyboardKey.digit5) { 37 | onKeypadPressed(DigitKey(5)); 38 | } else if (physicalKey == PhysicalKeyboardKey.numpad6 || physicalKey == PhysicalKeyboardKey.digit6) { 39 | onKeypadPressed(DigitKey(6)); 40 | } else if (physicalKey == PhysicalKeyboardKey.numpad7 || physicalKey == PhysicalKeyboardKey.digit7) { 41 | onKeypadPressed(DigitKey(7)); 42 | } else if (physicalKey == PhysicalKeyboardKey.numpad8 || physicalKey == PhysicalKeyboardKey.digit8) { 43 | onKeypadPressed(DigitKey(8)); 44 | } else if (physicalKey == PhysicalKeyboardKey.numpad9 || physicalKey == PhysicalKeyboardKey.digit9) { 45 | onKeypadPressed(DigitKey(9)); 46 | } else if (physicalKey == PhysicalKeyboardKey.numpadMultiply || logicalKey == LogicalKeyboardKey.asterisk) { 47 | onKeypadPressed(ActionKey.asterisk()); 48 | } else if (physicalKey == PhysicalKeyboardKey.numpadAdd || logicalKey == LogicalKeyboardKey.add) { 49 | onKeypadPressed(ActionKey.plus()); 50 | } else if (physicalKey == PhysicalKeyboardKey.numpadEnter) { 51 | onKeypadPressed(ActionKey.enter()); 52 | } else if (logicalKey == LogicalKeyboardKey.numberSign) { 53 | onKeypadPressed(ActionKey.hash()); 54 | } else { 55 | return KeyEventResult.ignored; 56 | } 57 | return KeyEventResult.handled; 58 | } else { 59 | return KeyEventResult.ignored; 60 | } 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return Focus( 66 | onKeyEvent: _handleOnKeyEvent, 67 | autofocus: true, 68 | child: child, 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/widgets/keypad_grid.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class KeypadGrid extends StatelessWidget { 4 | /// The number of items in the grid. 5 | final int itemCount; 6 | 7 | /// The [IndexedWidgetBuilder] for each item in the grid. 8 | final IndexedWidgetBuilder itemBuilder; 9 | 10 | /// The number of items in each row of the grid. 11 | final int crossAxisCount; 12 | 13 | /// The number of items in each row of the grid. 14 | final Widget? footer; 15 | 16 | const KeypadGrid({super.key, required this.itemBuilder, required this.itemCount, this.crossAxisCount = 3, this.footer}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | final length = (itemCount / crossAxisCount).floor(); 21 | 22 | List items = List.generate(length, (index) => index).map((e) { 23 | final subItems = List.generate( 24 | crossAxisCount, 25 | (index) { 26 | return Expanded( 27 | child: itemBuilder(context, e * crossAxisCount + index), 28 | ); 29 | }, 30 | ); 31 | return Expanded( 32 | child: Row(children: subItems), 33 | ); 34 | }).toList(); 35 | if (footer != null) 36 | items.add( 37 | Expanded(child: footer!), 38 | ); 39 | 40 | return Column(children: items); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/widgets/phone_text_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import '../mixins/scalable.dart'; 5 | import 'scalable/scalable.dart'; 6 | 7 | typedef CopyToClipboardBuilder = Widget Function(BuildContext context, VoidCallback onCopyPressed); 8 | 9 | class PhoneTextField extends StatelessWidget with Scalable { 10 | /// TextStyle for the text field. 11 | final TextStyle? textStyle; 12 | 13 | /// The background color of the text field. Defaults to [Colors.white]. 14 | final Color color; 15 | 16 | /// The text color of the text field. Defaults to [Colors.grey]. 17 | final Color textColor; 18 | 19 | /// Font size for the text field, as a percentage of the screen height. Defaults to 25. 20 | final double textSize; 21 | 22 | /// The decoration to show around the text field. Defaults to [InputDecoration(border: InputBorder.none)]. 23 | final InputDecoration decoration; 24 | 25 | /// Add copyToClipboard widget to the text field. Defaults to false. 26 | final bool copyToClipboard; 27 | 28 | /// The controller for the text field. 29 | final TextEditingController controller; 30 | 31 | /// Whether the text field is read only. Defaults to false. 32 | final bool readOnly; 33 | 34 | /// The alignment of the text field. Defaults to [TextAlign.center]. 35 | final TextAlign textAlign; 36 | 37 | /// Builder for the copyToClipboard widget. Defaults to [_defaultCopyToClipboardBuilder]. 38 | final CopyToClipboardBuilder? copyToClipboardBuilder; 39 | 40 | /// [ScalingType] for the button. Defaults to [ScalingType.fixed]. 41 | final ScalingType scalingType; 42 | 43 | /// [ScalingSize] for the button. Defaults to [ScalingSize.small]. 44 | final ScalingSize scalingSize; 45 | 46 | /// Minimum scaling size for the button content. Defaults to null. 47 | final double? minScalingSize; 48 | 49 | /// Maximum scaling size for the button content. Defaults to null. 50 | final double? maxScalingSize; 51 | 52 | const PhoneTextField({ 53 | super.key, 54 | this.textStyle, 55 | this.color = Colors.white, 56 | this.textColor = Colors.grey, 57 | this.textSize = 15, 58 | this.decoration = const InputDecoration(border: InputBorder.none), 59 | this.readOnly = false, 60 | this.textAlign = TextAlign.center, 61 | this.copyToClipboard = false, 62 | required this.controller, 63 | this.copyToClipboardBuilder, 64 | this.scalingType = ScalingType.fixed, 65 | this.scalingSize = ScalingSize.small, 66 | this.minScalingSize, 67 | this.maxScalingSize, 68 | }); 69 | 70 | void _onCopyPressed() { 71 | Clipboard.setData(ClipboardData(text: controller.text)); 72 | } 73 | 74 | Widget _defaultCopyToClipboardBuilder() { 75 | return IconButton(icon: Icon(Icons.copy), onPressed: _onCopyPressed); 76 | } 77 | 78 | @override 79 | Widget build(BuildContext context) { 80 | final screenSize = MediaQuery.of(context).size; 81 | final size = rescale(screenSize, scalingType, textSize, scalingSize: scalingSize, minClamp: minScalingSize, maxClamp: maxScalingSize); 82 | 83 | final _builtTextStyle = TextStyle( 84 | color: textColor, 85 | fontSize: size, 86 | ); 87 | 88 | final textField = TextFormField( 89 | style: textStyle ?? _builtTextStyle, 90 | decoration: decoration, 91 | readOnly: readOnly, 92 | textAlign: textAlign, 93 | controller: controller, 94 | ); 95 | 96 | if (copyToClipboard) { 97 | return Row( 98 | children: [ 99 | Expanded(child: textField), 100 | copyToClipboardBuilder?.call(context, _onCopyPressed) ?? _defaultCopyToClipboardBuilder(), 101 | ], 102 | ); 103 | } else { 104 | return textField; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/widgets/scalable/button_type.dart: -------------------------------------------------------------------------------- 1 | /// [ButtonType] is an enum that defines the shape of a button 2 | enum ButtonType { 3 | /// [ButtonType.rectangle] will clip the button to a rectangle e.g. an Windows/metro design 4 | rectangle, 5 | 6 | /// [ButtonType.circle] will clip the button to a circle e.g. an iPhone keypad 7 | circle; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/widgets/scalable/scalable.dart: -------------------------------------------------------------------------------- 1 | export 'scalable_button.dart'; 2 | export 'button_type.dart'; 3 | export 'scaling_type.dart'; 4 | export 'scaling_size.dart'; -------------------------------------------------------------------------------- /lib/src/widgets/scalable/scalable_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../utils/material_color.dart'; 4 | import 'button_type.dart'; 5 | 6 | /// Material Button with a scalable shape (clipping). 7 | class ScalableButton extends StatelessWidget { 8 | /// Whether to disable the button. Defaults to false. 9 | final bool disabled; 10 | 11 | /// Callback when the button is tapped. 12 | final GestureTapCallback? onPressed; 13 | 14 | /// Callback when the button is long pressed. 15 | final GestureLongPressCallback? onLongPressed; 16 | 17 | /// Button display style (clipping). Defaults to [ButtonType.rectangle]. 18 | /// [ButtonType.circle] will clip the button to a circle e.g. an iPhone keypad 19 | /// [ButtonType.rectangle] will clip the button to a rectangle e.g. an Android keypad 20 | final ButtonType buttonType; 21 | 22 | /// Background color of the button. Defaults to [Colors.grey]. 23 | final Color color; 24 | 25 | /// Padding around the button. Defaults to [EdgeInsets.all(12)]. 26 | final EdgeInsets padding; 27 | 28 | /// The child contained by the button. 29 | final Widget child; 30 | 31 | const ScalableButton({ 32 | super.key, 33 | required this.child, 34 | required this.onPressed, 35 | this.onLongPressed, 36 | this.color = Colors.grey, 37 | this.padding = EdgeInsets.zero, 38 | this.buttonType = ButtonType.rectangle, 39 | this.disabled = false, 40 | }); 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | // Use MaterialButton to get the Material ripple, splash and highlight colors including animations and gestures. 45 | return Container( 46 | clipBehavior: Clip.antiAlias, 47 | decoration: BoxDecoration( 48 | shape: buttonType == ButtonType.rectangle ? BoxShape.rectangle : BoxShape.circle, 49 | ), 50 | child: MaterialButton( 51 | color: createMaterialColor(color), 52 | onPressed: disabled ? null : onPressed, 53 | onLongPress: disabled ? null : onLongPressed, 54 | animationDuration: Duration(milliseconds: 300), 55 | child: Center( 56 | child: child, 57 | ), 58 | padding: padding, 59 | elevation: 0, 60 | highlightElevation: 0, 61 | hoverElevation: 0, 62 | ), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/widgets/scalable/scaling_size.dart: -------------------------------------------------------------------------------- 1 | /// Scaling Factor for the [ScalableButton] 2 | enum ScalingSize { 3 | /// Scalar value: 0.001 4 | smallest, 5 | 6 | /// Scalar value: 0.002 7 | smaller, 8 | 9 | /// Scalar value: 0.004 10 | small, 11 | 12 | /// Scalar value: 0.006 13 | medium, 14 | 15 | /// Scalar value: 0.008 16 | large, 17 | 18 | /// Scalar value: 0.009 19 | veryLarge, 20 | 21 | /// Scalar value: 0.01 22 | gigantic; 23 | 24 | double get scalar { 25 | switch (this) { 26 | case ScalingSize.smallest: 27 | return 0.001; 28 | case ScalingSize.smaller: 29 | return 0.002; 30 | case ScalingSize.small: 31 | return 0.004; 32 | case ScalingSize.medium: 33 | return 0.006; 34 | case ScalingSize.large: 35 | return 0.008; 36 | case ScalingSize.veryLarge: 37 | return 0.009; 38 | case ScalingSize.gigantic: 39 | return 0.01; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/widgets/scalable/scaling_type.dart: -------------------------------------------------------------------------------- 1 | /// The scaling type. 2 | enum ScalingType { 3 | /// Fixed width and height 4 | fixed, 5 | 6 | /// Fixed width, but scales to fit the screen height 7 | height, 8 | 9 | /// Fixed height, but scales to fit the screen width 10 | width, 11 | 12 | /// Scales to fit the screen width and height using the smallest scale factor 13 | min, 14 | 15 | /// Scales to fit the screen width and height using the largest scale factor 16 | max, 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'buttons/buttons.dart'; 2 | export 'keypad_grid.dart'; 3 | export 'phone_text_field.dart'; 4 | export 'keypad_focus_node.dart'; 5 | export 'scalable/scalable.dart'; -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.18.0" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.1" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_dtmf: 58 | dependency: "direct main" 59 | description: 60 | name: flutter_dtmf 61 | sha256: e95ed7262375d44c21de53b6ee01dd42fd470683bc224cafe82c4c9806df5787 62 | url: "https://pub.dev" 63 | source: hosted 64 | version: "3.0.0" 65 | flutter_masked_text2: 66 | dependency: "direct main" 67 | description: 68 | path: "." 69 | ref: "156f63b77d23148d9f1544230e3201ab6172f470" 70 | resolved-ref: "156f63b77d23148d9f1544230e3201ab6172f470" 71 | url: "https://github.com/cybex-dev/flutter_masked_text" 72 | source: git 73 | version: "0.9.1" 74 | flutter_test: 75 | dependency: "direct dev" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_web_plugins: 80 | dependency: transitive 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | matcher: 85 | dependency: transitive 86 | description: 87 | name: matcher 88 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "0.12.16" 92 | material_color_utilities: 93 | dependency: transitive 94 | description: 95 | name: material_color_utilities 96 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "0.5.0" 100 | meta: 101 | dependency: transitive 102 | description: 103 | name: meta 104 | sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "1.10.0" 108 | path: 109 | dependency: transitive 110 | description: 111 | name: path 112 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "1.8.3" 116 | plugin_platform_interface: 117 | dependency: transitive 118 | description: 119 | name: plugin_platform_interface 120 | sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "2.1.7" 124 | sky_engine: 125 | dependency: transitive 126 | description: flutter 127 | source: sdk 128 | version: "0.0.99" 129 | source_span: 130 | dependency: transitive 131 | description: 132 | name: source_span 133 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 134 | url: "https://pub.dev" 135 | source: hosted 136 | version: "1.10.0" 137 | stack_trace: 138 | dependency: transitive 139 | description: 140 | name: stack_trace 141 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 142 | url: "https://pub.dev" 143 | source: hosted 144 | version: "1.11.1" 145 | stream_channel: 146 | dependency: transitive 147 | description: 148 | name: stream_channel 149 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 150 | url: "https://pub.dev" 151 | source: hosted 152 | version: "2.1.2" 153 | string_scanner: 154 | dependency: transitive 155 | description: 156 | name: string_scanner 157 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 158 | url: "https://pub.dev" 159 | source: hosted 160 | version: "1.2.0" 161 | term_glyph: 162 | dependency: transitive 163 | description: 164 | name: term_glyph 165 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 166 | url: "https://pub.dev" 167 | source: hosted 168 | version: "1.2.1" 169 | test_api: 170 | dependency: transitive 171 | description: 172 | name: test_api 173 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 174 | url: "https://pub.dev" 175 | source: hosted 176 | version: "0.6.1" 177 | vector_math: 178 | dependency: transitive 179 | description: 180 | name: vector_math 181 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 182 | url: "https://pub.dev" 183 | source: hosted 184 | version: "2.1.4" 185 | web: 186 | dependency: transitive 187 | description: 188 | name: web 189 | sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 190 | url: "https://pub.dev" 191 | source: hosted 192 | version: "0.3.0" 193 | sdks: 194 | dart: ">=3.2.0-194.0.dev <4.0.0" 195 | flutter: ">=3.3.0" 196 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_dialpad 2 | description: A phone dialer widget that can be added to any Flutter Application to enable ability to dial a number. This could be combined with a voip application to enable placing calls. 3 | version: 1.1.0 4 | homepage: "https://www.eopeter.com" 5 | repository: "https://github.com/eopeter/flutter_dialpad" 6 | environment: 7 | sdk: ">=2.17.0 <4.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | # flutter_dtmf: ^2.0.2 14 | # Since flutter_masked_text2 is no longer supported, we use a forked version until a package with fix becomes available or an alternative is implemented 15 | flutter_dtmf: ^3.0.0 16 | flutter_masked_text2: 17 | git: 18 | url: https://github.com/cybex-dev/flutter_masked_text 19 | ref: 156f63b77d23148d9f1544230e3201ab6172f470 # branch 'fix_null_mask' 20 | #auto_size_text: ^2.0.2+1 21 | 22 | dev_dependencies: 23 | flutter_test: 24 | sdk: flutter 25 | 26 | # For information on the generic Dart part of this file, see the 27 | # following page: https://dart.dev/tools/pub/pubspec 28 | 29 | # The following section is specific to Flutter. 30 | flutter: 31 | 32 | # To add assets to your package, add an assets section, like this: 33 | # assets: 34 | # - images/a_dot_burr.jpeg 35 | # - images/a_dot_ham.jpeg 36 | # 37 | # For details regarding assets in packages, see 38 | # https://flutter.dev/assets-and-images/#from-packages 39 | # 40 | # An image asset can refer to one or more resolution-specific "variants", see 41 | # https://flutter.dev/assets-and-images/#resolution-aware. 42 | 43 | # To add custom fonts to your package, add a fonts section here, 44 | # in this "flutter" section. Each entry in this list should have a 45 | # "family" key with the font family name, and a "fonts" key with a 46 | # list giving the asset and other descriptors for the font. For 47 | # example: 48 | # fonts: 49 | # - family: Schyler 50 | # fonts: 51 | # - asset: fonts/Schyler-Regular.ttf 52 | # - asset: fonts/Schyler-Italic.ttf 53 | # style: italic 54 | # - family: Trajan Pro 55 | # fonts: 56 | # - asset: fonts/TrajanPro.ttf 57 | # - asset: fonts/TrajanPro_Bold.ttf 58 | # weight: 700 59 | # 60 | # For details regarding fonts in packages, see 61 | # https://flutter.dev/custom-fonts/#from-packages 62 | -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eopeter/flutter_dialpad/f409ea118ff0874cfc30e6943499af6c5371b65f/screenshots/screenshot2.png -------------------------------------------------------------------------------- /test/flutter_dialpad_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | void main() { 4 | test('adds one to input values', () { 5 | 6 | }); 7 | } 8 | --------------------------------------------------------------------------------