Add flutter_lints to form_app (#822)

pull/833/head
Brett Morgan 3 years ago committed by GitHub
parent fc1fe989fb
commit 12ba3b2245
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,19 @@
include: package:flutter_lints/flutter.yaml
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
linter:
rules:
avoid_types_on_closure_parameters: true
avoid_void_async: true
cancel_subscriptions: true
close_sinks: true
directives_ordering: true
package_api_docs: true
package_prefixed_library_names: true
test_types_in_equals: true
throw_in_finally: true
unnecessary_statements: true

@ -26,21 +26,17 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion 30
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
lintOptions {
disable 'InvalidPackage'
}
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "dev.flutter.formApp.form_app" applicationId "dev.flutter.formApp.form_app"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 30
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
} }

@ -1,12 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.flutter.formApp.form_app"> package="dev.flutter.formApp.form_app">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that <application
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="form_app" android:label="form_app"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Theme applied to the Android Window while the process is starting --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
@ -12,7 +12,7 @@
running. running.
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item> <item name="android:windowBackground">?android:colorBackground</item>
</style> </style>
</resources> </resources>

@ -6,7 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.0' classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
@ -21,8 +21,6 @@ allprojects {
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

@ -1,7 +1,3 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
include ':app' include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def localPropertiesFile = new File(rootProject.projectDir, "local.properties")

@ -18,6 +18,7 @@ Flutter/App.framework
Flutter/Flutter.framework Flutter/Flutter.framework
Flutter/Flutter.podspec Flutter/Flutter.podspec
Flutter/Generated.xcconfig Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx Flutter/app.flx
Flutter/app.zip Flutter/app.zip
Flutter/flutter_assets/ Flutter/flutter_assets/

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>App</string> <string>App</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>

@ -272,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -289,16 +289,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -354,7 +346,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -403,7 +395,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -421,16 +413,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -448,16 +432,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.formApp.formApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

@ -2,6 +2,6 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "self:">
</FileRef> </FileRef>
</Workspace> </Workspace>

@ -15,7 +15,7 @@ import 'src/validation.dart';
final http.Client httpClient = MockClient(); final http.Client httpClient = MockClient();
void main() { void main() {
runApp(FormApp()); runApp(const FormApp());
} }
final demos = [ final demos = [
@ -29,41 +29,44 @@ final demos = [
Demo( Demo(
name: 'Autofill', name: 'Autofill',
route: '/autofill', route: '/autofill',
builder: (context) => AutofillDemo(), builder: (context) => const AutofillDemo(),
), ),
Demo( Demo(
name: 'Form widgets', name: 'Form widgets',
route: '/form_widgets', route: '/form_widgets',
builder: (context) => FormWidgetsDemo(), builder: (context) => const FormWidgetsDemo(),
), ),
Demo( Demo(
name: 'Validation', name: 'Validation',
route: '/validation', route: '/validation',
builder: (context) => FormValidationDemo(), builder: (context) => const FormValidationDemo(),
), ),
]; ];
class FormApp extends StatelessWidget { class FormApp extends StatelessWidget {
const FormApp({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Form Samples', title: 'Form Samples',
theme: ThemeData(primarySwatch: Colors.teal), theme: ThemeData(primarySwatch: Colors.teal),
routes: Map.fromEntries(demos.map((d) => MapEntry(d.route, d.builder))), routes: Map.fromEntries(demos.map((d) => MapEntry(d.route, d.builder))),
home: HomePage(), home: const HomePage(),
); );
} }
} }
class HomePage extends StatelessWidget { class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Form Samples'), title: const Text('Form Samples'),
), ),
body: ListView( body: ListView(
children: [...demos.map((d) => DemoTile(d))], children: [...demos.map((d) => DemoTile(demo: d))],
), ),
); );
} }
@ -72,7 +75,7 @@ class HomePage extends StatelessWidget {
class DemoTile extends StatelessWidget { class DemoTile extends StatelessWidget {
final Demo demo; final Demo demo;
DemoTile(this.demo); const DemoTile({this.demo, Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

@ -7,6 +7,8 @@ import 'package:flutter/material.dart';
// Demonstrates how to use autofill hints. The full list of hints is here: // Demonstrates how to use autofill hints. The full list of hints is here:
// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart // https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart
class AutofillDemo extends StatefulWidget { class AutofillDemo extends StatefulWidget {
const AutofillDemo({Key key}) : super(key: key);
@override @override
_AutofillDemoState createState() => _AutofillDemoState(); _AutofillDemoState createState() => _AutofillDemoState();
} }
@ -18,36 +20,36 @@ class _AutofillDemoState extends State<AutofillDemo> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Autofill'), title: const Text('Autofill'),
), ),
body: Form( body: Form(
key: _formKey, key: _formKey,
child: Scrollbar( child: Scrollbar(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: AutofillGroup( child: AutofillGroup(
child: Column( child: Column(
children: [ children: [
...[ ...[
Text('This sample demonstrates autofill. '), const Text('This sample demonstrates autofill. '),
TextFormField( TextFormField(
autofocus: true, autofocus: true,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: const InputDecoration(
hintText: 'Jane', hintText: 'Jane',
labelText: 'First Name', labelText: 'First Name',
), ),
autofillHints: [AutofillHints.givenName], autofillHints: const [AutofillHints.givenName],
), ),
TextFormField( TextFormField(
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: const InputDecoration(
hintText: 'Doe', hintText: 'Doe',
labelText: 'Last Name', labelText: 'Last Name',
), ),
autofillHints: [AutofillHints.familyName], autofillHints: const [AutofillHints.familyName],
), ),
TextField( const TextField(
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: InputDecoration(
@ -56,7 +58,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
), ),
autofillHints: [AutofillHints.email], autofillHints: [AutofillHints.email],
), ),
TextField( const TextField(
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: InputDecoration(
@ -65,7 +67,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
), ),
autofillHints: <String>[AutofillHints.telephoneNumber], autofillHints: <String>[AutofillHints.telephoneNumber],
), ),
TextField( const TextField(
keyboardType: TextInputType.streetAddress, keyboardType: TextInputType.streetAddress,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: InputDecoration(
@ -74,7 +76,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
), ),
autofillHints: <String>[AutofillHints.streetAddressLine1], autofillHints: <String>[AutofillHints.streetAddressLine1],
), ),
TextField( const TextField(
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: InputDecoration(
@ -83,7 +85,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
), ),
autofillHints: <String>[AutofillHints.postalCode], autofillHints: <String>[AutofillHints.postalCode],
), ),
TextField( const TextField(
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'United States', hintText: 'United States',
@ -91,7 +93,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
), ),
autofillHints: <String>[AutofillHints.countryName], autofillHints: <String>[AutofillHints.countryName],
), ),
TextField( const TextField(
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '1', hintText: '1',
@ -102,7 +104,7 @@ class _AutofillDemoState extends State<AutofillDemo> {
].expand( ].expand(
(widget) => [ (widget) => [
widget, widget,
SizedBox( const SizedBox(
height: 24, height: 24,
) )
], ],

@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl; import 'package:intl/intl.dart' as intl;
class FormWidgetsDemo extends StatefulWidget { class FormWidgetsDemo extends StatefulWidget {
const FormWidgetsDemo({Key key}) : super(key: key);
@override @override
_FormWidgetsDemoState createState() => _FormWidgetsDemoState(); _FormWidgetsDemoState createState() => _FormWidgetsDemoState();
} }
@ -23,7 +25,7 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Form widgets'), title: const Text('Form widgets'),
), ),
body: Form( body: Form(
key: _formKey, key: _formKey,
@ -32,16 +34,16 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: Card( child: Card(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 400), constraints: const BoxConstraints(maxWidth: 400),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
...[ ...[
TextFormField( TextFormField(
decoration: InputDecoration( decoration: const InputDecoration(
filled: true, filled: true,
hintText: 'Enter a title...', hintText: 'Enter a title...',
labelText: 'Title', labelText: 'Title',
@ -53,8 +55,8 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
}, },
), ),
TextFormField( TextFormField(
decoration: InputDecoration( decoration: const InputDecoration(
border: const OutlineInputBorder(), border: OutlineInputBorder(),
filled: true, filled: true,
hintText: 'Enter a description...', hintText: 'Enter a description...',
labelText: 'Description', labelText: 'Description',
@ -64,7 +66,7 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
}, },
maxLines: 5, maxLines: 5,
), ),
_FormDatePicker( _FormDatePicker<DateTime>(
date: date, date: date,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -139,7 +141,7 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
].expand( ].expand(
(widget) => [ (widget) => [
widget, widget,
SizedBox( const SizedBox(
height: 24, height: 24,
) )
], ],
@ -156,11 +158,11 @@ class _FormWidgetsDemoState extends State<FormWidgetsDemo> {
} }
} }
class _FormDatePicker extends StatefulWidget { class _FormDatePicker<T> extends StatefulWidget {
final DateTime date; final DateTime date;
final ValueChanged onChanged; final ValueChanged<T> onChanged;
_FormDatePicker({ const _FormDatePicker({
this.date, this.date,
this.onChanged, this.onChanged,
}); });
@ -191,7 +193,7 @@ class _FormDatePickerState extends State<_FormDatePicker> {
], ],
), ),
TextButton( TextButton(
child: Text('Edit'), child: const Text('Edit'),
onPressed: () async { onPressed: () async {
var newDate = await showDatePicker( var newDate = await showDatePicker(
context: context, context: context,

@ -11,10 +11,10 @@ class MockClient extends Mock implements http.Client {
MockClient() { MockClient() {
when(post('https://example.com/signin', body: anyNamed('body'))) when(post('https://example.com/signin', body: anyNamed('body')))
.thenAnswer((answering) { .thenAnswer((answering) {
var body = answering.namedArguments[Symbol('body')]; dynamic body = answering.namedArguments[const Symbol('body')];
if (body != null && body is String) { if (body != null && body is String) {
var decodedJson = json.decode(body); var decodedJson = json.decode(body) as Map<String, String>;
if (decodedJson['email'] == 'root' && if (decodedJson['email'] == 'root' &&
decodedJson['password'] == 'password') { decodedJson['password'] == 'password') {

@ -29,9 +29,10 @@ class FormData {
class SignInHttpDemo extends StatefulWidget { class SignInHttpDemo extends StatefulWidget {
final http.Client httpClient; final http.Client httpClient;
SignInHttpDemo({ const SignInHttpDemo({
this.httpClient, this.httpClient,
}); Key key,
}) : super(key: key);
@override @override
_SignInHttpDemoState createState() => _SignInHttpDemoState(); _SignInHttpDemoState createState() => _SignInHttpDemoState();
@ -44,19 +45,19 @@ class _SignInHttpDemoState extends State<SignInHttpDemo> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Sign in Form'), title: const Text('Sign in Form'),
), ),
body: Form( body: Form(
child: Scrollbar( child: Scrollbar(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
children: [ children: [
...[ ...[
TextFormField( TextFormField(
autofocus: true, autofocus: true,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: InputDecoration( decoration: const InputDecoration(
filled: true, filled: true,
hintText: 'Your email address', hintText: 'Your email address',
labelText: 'Email', labelText: 'Email',
@ -66,7 +67,7 @@ class _SignInHttpDemoState extends State<SignInHttpDemo> {
}, },
), ),
TextFormField( TextFormField(
decoration: InputDecoration( decoration: const InputDecoration(
filled: true, filled: true,
labelText: 'Password', labelText: 'Password',
), ),
@ -76,7 +77,7 @@ class _SignInHttpDemoState extends State<SignInHttpDemo> {
}, },
), ),
TextButton( TextButton(
child: Text('Sign in'), child: const Text('Sign in'),
onPressed: () async { onPressed: () async {
// Use a JSON encoded string to send // Use a JSON encoded string to send
var result = await widget.httpClient.post( var result = await widget.httpClient.post(
@ -96,7 +97,7 @@ class _SignInHttpDemoState extends State<SignInHttpDemo> {
].expand( ].expand(
(widget) => [ (widget) => [
widget, widget,
SizedBox( const SizedBox(
height: 24, height: 24,
) )
], ],
@ -110,13 +111,13 @@ class _SignInHttpDemoState extends State<SignInHttpDemo> {
} }
void _showDialog(String message) { void _showDialog(String message) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: Text(message), title: Text(message),
actions: [ actions: [
TextButton( TextButton(
child: Text('OK'), child: const Text('OK'),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
], ],

@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart' as english_words; import 'package:english_words/english_words.dart' as english_words;
class FormValidationDemo extends StatefulWidget { class FormValidationDemo extends StatefulWidget {
const FormValidationDemo({Key key}) : super(key: key);
@override @override
_FormValidationDemoState createState() => _FormValidationDemoState(); _FormValidationDemoState createState() => _FormValidationDemoState();
} }
@ -20,13 +22,13 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('📖 Story Generator'), title: const Text('📖 Story Generator'),
actions: [ actions: [
Padding( Padding(
padding: EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: TextButton( child: TextButton(
style: TextButton.styleFrom(primary: Colors.white), style: TextButton.styleFrom(primary: Colors.white),
child: Text('Submit'), child: const Text('Submit'),
onPressed: () { onPressed: () {
// Validate the form by getting the FormState from the GlobalKey // Validate the form by getting the FormState from the GlobalKey
// and calling validate() on it. // and calling validate() on it.
@ -35,14 +37,14 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
return; return;
} }
showDialog( showDialog<void>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: Text('Your story'), title: const Text('Your story'),
content: Text('The $adjective developer saw a $noun'), content: Text('The $adjective developer saw a $noun'),
actions: [ actions: [
TextButton( TextButton(
child: Text('Done'), child: const Text('Done'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
@ -59,7 +61,7 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
key: _formKey, key: _formKey,
child: Scrollbar( child: Scrollbar(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
children: [ children: [
// A text field that validates that the text is an adjective. // A text field that validates that the text is an adjective.
@ -75,7 +77,7 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
} }
return 'Not a valid adjective.'; return 'Not a valid adjective.';
}, },
decoration: InputDecoration( decoration: const InputDecoration(
filled: true, filled: true,
hintText: 'e.g. quick, beautiful, interesting', hintText: 'e.g. quick, beautiful, interesting',
labelText: 'Enter an adjective', labelText: 'Enter an adjective',
@ -84,7 +86,7 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
adjective = value; adjective = value;
}, },
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
// A text field that validates that the text is a noun. // A text field that validates that the text is a noun.
@ -98,7 +100,7 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
} }
return 'Not a valid noun.'; return 'Not a valid noun.';
}, },
decoration: InputDecoration( decoration: const InputDecoration(
filled: true, filled: true,
hintText: 'i.e. a person, place or thing', hintText: 'i.e. a person, place or thing',
labelText: 'Enter a noun', labelText: 'Enter a noun',
@ -107,12 +109,12 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
noun = value; noun = value;
}, },
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
// A custom form field that requires the user to check a // A custom form field that requires the user to check a
// checkbox. // checkbox.
FormField( FormField<bool>(
initialValue: false, initialValue: false,
validator: (value) { validator: (value) {
if (value == false) { if (value == false) {
@ -120,7 +122,7 @@ class _FormValidationDemoState extends State<FormValidationDemo> {
} }
return null; return null;
}, },
builder: (FormFieldState formFieldState) { builder: (formFieldState) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

@ -202,6 +202,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -277,6 +284,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.5.1" version: "3.5.1"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:

@ -1,6 +1,6 @@
name: form_app name: form_app
description: A sample demonstrating different types of forms and best practices description: A sample demonstrating different types of forms and best practices
publish_to: 'none' publish_to: "none"
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
@ -21,5 +21,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
json_serializable: ^3.0.0 json_serializable: ^3.0.0
build_runner: ^1.10.0 build_runner: ^1.10.0
flutter_lints: ^1.0.0
flutter: flutter:
uses-material-design: true uses-material-design: true

@ -1,6 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
-->
<base href="/">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A sample demonstrating different types of forms and best practices"> <meta name="description" content="A sample demonstrating different types of forms and best practices">
@ -11,23 +24,78 @@
<meta name="apple-mobile-web-app-title" content="form_app"> <meta name="apple-mobile-web-app-title" content="form_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
<title>form_app</title> <title>form_app</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
</head> </head>
<body> <body>
<!-- This script installs service_worker.js to provide PWA functionality to <!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see: application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers --> https://developers.google.com/web/fundamentals/primers/service-workers -->
<script> <script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () { window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js'); // Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
}); });
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
} }
</script> </script>
<script src="main.dart.js" type="application/javascript"></script>
</body> </body>
</html> </html>
Loading…
Cancel
Save