Skip to content

Commit

Permalink
💚 Conform Dart team's pedantic linting rule and then some
Browse files Browse the repository at this point in the history
  • Loading branch information
toureholder committed Jun 21, 2019
1 parent b5804bb commit ccf6b86
Show file tree
Hide file tree
Showing 43 changed files with 418 additions and 364 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ install:
- flutter doctor -v
- flutter packages get
script:
- flutter analyze --no-pub --no-current-package lib/ test/
- flutter test --coverage --coverage-path=lcov.info
after_success:
- bash <(curl -s https://codecov.io/bash)
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

[![Build Status](https://travis-ci.com/toureh/flutter_workshop.svg?branch=master)](https://travis-ci.com/toureh/flutter_workshop)
[![codecov](https://codecov.io/gh/toureh/flutter_workshop/branch/master/graph/badge.svg)](https://codecov.io/gh/toureh/flutter_workshop)
[![Lint](https://img.shields.io/badge/style-pedantic-blue.svg)](https://github.com/dart-lang/pedantic)

This repository contains a sample app the I use to give workshops geared towards beginners to Flutter. I've made an effort to keep the git history clean so that each commit represents a significant building block in the app.
This repository contains a sample app that I use to give workshops geared towards beginners to Flutter. I've made an effort to keep the git history clean by doing cohesive commits that each represent a teachable building block in the app.


## Contains examples of:

* Basic widgets
* Building layouts
* Basic widgets and building layouts
* Internationalization
* Navigation & routing
* Networking
Expand All @@ -18,6 +18,7 @@ This repository contains a sample app the I use to give workshops geared towards
* Storing key-value data on disk
* Dependency injection with InheritedWidget
* Unit and widget tests
* Configuring linting rules to implement the Effective Dart guidelines
* Continuous integration pipelines with Travis CI
* Uploading code coverage to Codecov

Expand Down
49 changes: 49 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# All Google internally enforced app development rules and some Flutter SDK development rules

# https://raw.githubusercontent.com/dart-lang/pedantic/master/lib/analysis_options.1.7.0.yaml
# https://github.com/flutter/flutter/blob/master/analysis_options.yaml

linter:
rules:
- always_declare_return_types # Flutter SDK style
- avoid_empty_else
- avoid_init_to_null
- avoid_relative_lib_imports
- avoid_return_types_on_setters
- avoid_shadowing_type_parameters
- avoid_types_as_parameter_names
- await_only_futures # Flutter SDK style
- camel_case_types # Flutter SDK style
- cancel_subscriptions # Flutter SDK style
- curly_braces_in_flow_control_structures
- empty_catches
- empty_constructor_bodies
- library_names
- library_prefixes
- no_duplicate_case_values
- null_closures
- prefer_const_constructors # Flutter SDK style
- prefer_const_constructors_in_immutables # Flutter SDK style
- prefer_const_declarations # Flutter SDK style
- prefer_const_literals_to_create_immutables # Flutter SDK style
- prefer_contains
- prefer_equal_for_default_values
- prefer_is_empty
- prefer_is_not_empty
- recursive_getters
- slash_for_doc_comments
- type_init_formals
- unawaited_futures
- unnecessary_brace_in_string_interps # Flutter SDK style
- unnecessary_const # Flutter SDK style
- unnecessary_getters_setters # Flutter SDK style
- unnecessary_new # Flutter SDK style
- unnecessary_null_aware_assignments # Flutter SDK style
- unnecessary_null_in_if_null_operators
- unnecessary_overrides # Flutter SDK style
- unnecessary_parenthesis # Flutter SDK style
- unnecessary_statements # Flutter SDK style
- unnecessary_this # Flutter SDK style
- unrelated_type_equality_checks
- use_rethrow_when_possible
- valid_regexps
7 changes: 3 additions & 4 deletions lib/base/base_api.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import 'package:http/http.dart' as http;
import 'dart:convert';

import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';

class BaseApi {
final http.Client client;

BaseApi({@required this.client});

final http.Client client;

final String baseUrl = 'https://giv-api.herokuapp.com/';

Future<http.Response> get(String url) =>
Expand Down
6 changes: 3 additions & 3 deletions lib/base/base_material_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ class BaseMaterialApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: [
const StringLocalizationsDelegate(),
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
StringLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: AppLocales.supportedLocales,
supportedLocales: supportedLocales,
home: Home(),
);
}
Expand Down
9 changes: 4 additions & 5 deletions lib/base/dependency_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@ class DependencyProvider extends InheritedWidget {
final AppDependencies dependencies;

static DependencyProvider of(BuildContext context) {
return context.inheritFromWidgetOfExactType(DependencyProvider)
as DependencyProvider;
return context.inheritFromWidgetOfExactType(DependencyProvider);
}

@override
bool updateShouldNotify(DependencyProvider old) => true;
bool updateShouldNotify(DependencyProvider oldWidget) => true;
}

class AppDependencies {
AppDependencies({this.loginBloc, this.homeBloc});

final LoginBloc loginBloc;
final HomeBloc homeBloc;

AppDependencies({this.loginBloc, this.homeBloc});
}
4 changes: 2 additions & 2 deletions lib/base/my_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import 'package:flutter_workshop/base/base_material_app.dart';
import 'package:flutter_workshop/base/dependency_provider.dart';

class MyApp extends StatefulWidget {
final AppDependencies dependencies;

const MyApp({Key key, this.dependencies}) : super(key: key);

final AppDependencies dependencies;

@override
_MyAppState createState() => _MyAppState();
}
Expand Down
24 changes: 11 additions & 13 deletions lib/config/l10n.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_workshop/values/strings.dart';

const Iterable<Locale> supportedLocales = <Locale>[
Locale('en'),
Locale('pt'),
];

List<String> get supportedLanguageCodes =>
supportedLocales.map((Locale locale) => locale.languageCode).toList();

class StringLocalizationsDelegate extends LocalizationsDelegate<L10n> {
const StringLocalizationsDelegate();

@override
bool isSupported(Locale locale) =>
AppLocales.supportedLanguageCodes.contains(locale.languageCode);
supportedLanguageCodes.contains(locale.languageCode);

@override
Future<L10n> load(Locale locale) => SynchronousFuture<L10n>(L10n(locale));
Expand All @@ -20,8 +28,8 @@ class L10n {
L10n(this._locale);

final Locale _locale;
final String defaultLanguageCode = AppLocales.supportedLanguageCodes.first;
static Map<String, Map<String, String>> _localizedValues = Strings().map;
final String defaultLanguageCode = supportedLanguageCodes.first;
final Map<String, Map<String, String>> _localizedValues = Strings().map;

String _get(String key) {
if (_localizedValues[key] == null) return key;
Expand All @@ -34,13 +42,3 @@ class L10n {
static String getString(BuildContext context, String key) =>
key == null ? null : Localizations.of<L10n>(context, L10n)._get(key);
}

class AppLocales {
static const supportedLocales = [
const Locale('en'),
const Locale('pt'),
];

static List<String> get supportedLanguageCodes =>
supportedLocales.map((locale) => locale.languageCode).toList();
}
22 changes: 11 additions & 11 deletions lib/custom/custom_alert_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_workshop/config/l10n.dart';

class CustomAlertDialog extends StatelessWidget {
final VoidCallback onConfirmed;
final String confirmationText;
final String cancellationText;
final String titleText;
final String contentText;
final bool hasCancelButton;

const CustomAlertDialog(
{Key key,
this.onConfirmed,
Expand All @@ -19,10 +12,17 @@ class CustomAlertDialog extends StatelessWidget {
this.hasCancelButton = false})
: super(key: key);

final VoidCallback onConfirmed;
final String confirmationText;
final String cancellationText;
final String titleText;
final String contentText;
final bool hasCancelButton;

@override
Widget build(BuildContext context) {
final title = titleText == null ? null : Text(titleText);
final content = contentText == null ? null : Text(contentText);
final Text title = titleText == null ? null : Text(titleText);
final Text content = contentText == null ? null : Text(contentText);

return AlertDialog(
title: title,
Expand All @@ -37,12 +37,12 @@ class CustomAlertDialog extends StatelessWidget {
);
}

Widget _cancelButton(context) => FlatButton(
Widget _cancelButton(BuildContext context) => FlatButton(
child: Text(
cancellationText ?? L10n.getString(context, 'common_cancel'),
),
onPressed: () => _closeDialog(context),
);

_closeDialog(context) => Navigator.of(context).pop();
bool _closeDialog(BuildContext context) => Navigator.of(context).pop();
}
21 changes: 12 additions & 9 deletions lib/custom/custom_app_bar.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import 'package:flutter/material.dart';

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
const CustomAppBar({Key key, this.actions, this.title}) : super(key: key);

final List<Widget> actions;
final String title;

const CustomAppBar({Key key, this.actions, this.title}) : super(key: key);

@override
Widget build(BuildContext context) {
final computedTitle = title == null ? '' : title;
final String computedTitle = title == null ? '' : title;

return AppBar(
brightness: Brightness.light,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.grey),
title: Text(computedTitle, style: TextStyle(color: Colors.grey[800]),),
iconTheme: const IconThemeData(color: Colors.grey),
title: Text(
computedTitle,
style: TextStyle(color: Colors.grey[800]),
),
elevation: 0,
actions: actions,
bottom: CustomAppBarBottom(),
);
}

@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

class CustomAppBarBottom extends StatelessWidget implements PreferredSizeWidget {
class CustomAppBarBottom extends StatelessWidget
implements PreferredSizeWidget {
@override
Widget build(BuildContext context) {
return Container(
Expand All @@ -35,6 +39,5 @@ class CustomAppBarBottom extends StatelessWidget implements PreferredSizeWidget
}

@override
Size get preferredSize => Size.fromHeight(1);
Size get preferredSize => const Size.fromHeight(1);
}

28 changes: 11 additions & 17 deletions lib/feature/detail/detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import 'package:flutter_workshop/model/donation/donation.dart';
import 'package:flutter_workshop/model/user/user.dart';

class Detail extends StatelessWidget {
final Donation donation;

const Detail({Key key, this.donation}) : super(key: key);

final Donation donation;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(),
appBar: const CustomAppBar(),
body: _body(),
);
}

Widget _body() {
return Container(
padding: EdgeInsets.all(20),
padding: const EdgeInsets.all(20),
child: ListView(
children: <Widget>[
_carousel(),
Expand All @@ -34,9 +34,9 @@ class Detail extends StatelessWidget {
}

Row _user(User user) {
final backgroundImage =
final ImageProvider backgroundImage =
user.avatarUrl == null ? null : NetworkImage(user.avatarUrl);
final child =
final Widget child =
user.avatarUrl == null ? Text(user.name[0].toUpperCase()) : null;

return Row(
Expand All @@ -47,19 +47,13 @@ class Detail extends StatelessWidget {
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
SizedBox(
width: 16,
),
const SizedBox(width: 16),
Text(user.name)
],
);
}

SizedBox _verticalMargin() {
return SizedBox(
height: 24,
);
}
SizedBox _verticalMargin() => const SizedBox(height: 24);

Text _description() => Text(donation.description);

Expand All @@ -69,14 +63,14 @@ class Detail extends StatelessWidget {
);

Widget _carousel() {
final size = 300.0;
const double size = 300.0;
return SizedBox(
height: size,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: donation.images.length,
itemBuilder: (context, index) {
String url = donation.images[index].url;
itemBuilder: (BuildContext context, int index) {
final String url = donation.images[index].url;
return _image(url, size);
}),
);
Expand Down
Loading

0 comments on commit ccf6b86

Please sign in to comment.