Fix sample index deployment action (#862)

* Update sample index dependencies

* Update to tuneup 0.3.8, update dependencies

* Upgrade to null safety, lock sass version

* fix analyzer warnings

* Fix unit tests

* Fix issues from upgrading to null safety
pull/863/head
John Ryan 3 years ago committed by GitHub
parent 699cc3a8c5
commit 7de8fafcee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -28,7 +28,7 @@ packages:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.1"
version: "1.7.2"
args:
dependency: transitive
description:
@ -42,7 +42,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
version: "2.7.0"
boolean_selector:
dependency: transitive
description:
@ -63,7 +63,7 @@ packages:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
cli_util:
dependency: transitive
description:
@ -149,6 +149,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
glob:
dependency: transitive
description:
@ -211,7 +218,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.7.0"
mime:
dependency: transitive
description:
@ -370,21 +377,21 @@ packages:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.8"
version: "1.17.9"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.1"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.19"
version: "0.3.29"
typed_data:
dependency: transitive
description:

@ -7,14 +7,14 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
lints:
dependency: "direct dev"
description:
@ -35,7 +35,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
version: "1.7.0"
path:
dependency: "direct main"
description:

@ -12,7 +12,7 @@ import 'package:html/parser.dart' show parse;
import 'package:path/path.dart' as path;
class CookbookScraper {
WebDriver _driver;
late WebDriver _driver;
Future init() async {
_driver = await createDriver(desired: <String, dynamic>{});
@ -36,7 +36,12 @@ class CookbookScraper {
await _driver.get(Uri.parse(url));
var pageContent = await _driver.pageSource;
var page = parse(pageContent);
var name = page.querySelector('main>.container>header>h1').text;
var search = 'main>.container>header>h1';
var h1 = page.querySelector(search);
if (h1 == null) {
throw ('Could not find match for $search on page $url');
}
var name = h1.text;
var description = page.querySelectorAll('main>.container>p').first.text;
var urlSegments = Uri.parse(url).pathSegments;
@ -50,6 +55,7 @@ class CookbookScraper {
screenshots: [Screenshot(screenshotPath(url), 'Cookbook article')],
tags: ['cookbook', category],
source: url,
difficulty: 'advanced',
);
}

@ -15,8 +15,10 @@ Future<List<Sample>> getSamples() async {
var cookbookFile = File('lib/src/cookbook.json');
var contents = await yamlFile.readAsString();
var cookbookContents = await cookbookFile.readAsString();
var index = checkedYamlDecode(contents, (m) => Index.fromJson(m),
var index = checkedYamlDecode(
contents, (m) => m != null ? Index.fromJson(m) : null,
sourceUrl: yamlFile.uri);
if (index == null) throw('unable to get load from ${yamlFile.uri}');
var cookbookIndex =
Index.fromJson(json.decode(cookbookContents) as Map<dynamic, dynamic>);
return index.samples..addAll(cookbookIndex.samples);

@ -7,14 +7,15 @@ import 'dart:html';
class Carousel {
final bool withArrowKeyControl;
final Element container = querySelector('.slider-container');
final Element container = querySelector('.slider-container')!;
final List<Element> slides = querySelectorAll('.slider-single');
int currentSlideIndex, lastSlideIndex;
late int currentSlideIndex;
late int lastSlideIndex;
Element prevSlide, currentSlide, nextSlide;
late Element prevSlide, currentSlide, nextSlide;
num x0;
late num x0;
bool touched = false;
Carousel.init({this.withArrowKeyControl = false}) {
@ -90,13 +91,13 @@ class Carousel {
}
void _touchStartListener(TouchEvent e) {
x0 = e.changedTouches.first.client.x;
x0 = e.changedTouches!.first.client.x;
touched = true;
}
void _touchEndListener(TouchEvent e) {
if (touched) {
int dx = (e.changedTouches.first.client.x - x0) as int;
int dx = (e.changedTouches!.first.client.x - x0) as int;
// dx==0 case is ignored
if (dx > 0 && currentSlideIndex > 0) {
@ -115,7 +116,7 @@ class Carousel {
void _updateBullets() {
final bullets =
querySelector('.bullet-container').querySelectorAll('.bullet');
querySelector('.bullet-container')!.querySelectorAll('.bullet');
for (var i = 0; i < bullets.length; i++) {
bullets[i].classes.remove('active');
if (i == currentSlideIndex) {
@ -132,18 +133,18 @@ class Carousel {
if (currentSlideIndex == slides.length - 1) {
slides[0].classes.add('hidden');
slides[slides.length - 1].classes.remove('hidden');
prevArrow.classes.remove('hidden');
nextArrow.classes.add('hidden');
prevArrow!.classes.remove('hidden');
nextArrow!.classes.add('hidden');
} else if (currentSlideIndex == 0) {
slides[slides.length - 1].classes.add('hidden');
slides[0].classes.remove('hidden');
prevArrow.classes.add('hidden');
nextArrow.classes.remove('hidden');
prevArrow!.classes.add('hidden');
nextArrow!.classes.remove('hidden');
} else {
slides[slides.length - 1].classes.remove('hidden');
slides[0].classes.remove('hidden');
prevArrow.classes.remove('hidden');
nextArrow.classes.remove('hidden');
prevArrow!.classes.remove('hidden');
nextArrow!.classes.remove('hidden');
}
}

@ -34,7 +34,7 @@ class Sample {
final String name;
/// The author of the sample. Typically "Flutter"
final String author;
final String? author;
/// Screenshots of the sample or cookbook article. At least 1 screenshot is
/// required.
@ -44,14 +44,14 @@ class Sample {
final String source;
/// A link to this sample running in the browser.
final String web;
final String? web;
/// 3-5 sentences describing the sample.
final String description;
/// The difficulty level. Values are either 'beginner', 'intermediate', or
/// 'advanced'.
final String difficulty;
final String? difficulty;
/// List of widgets or Flutter APIs used by the sample. e.g. "AnimatedBuilder"
/// or "ChangeNotifier".
@ -76,26 +76,26 @@ class Sample {
final String type;
/// The date this sample was created.
final DateTime date;
final DateTime? date;
/// The Flutter channel this sample runs on. Either 'stable', 'dev' or
/// 'master'.
final String channel;
final String? channel;
Sample({
this.name,
this.author,
this.screenshots,
this.source,
required this.name,
this.author = 'Flutter',
required this.screenshots,
required this.source,
this.web,
this.description,
this.difficulty,
required this.description,
this.difficulty = 'beginner',
this.widgets = const [],
this.packages = const [],
this.tags = const [],
this.platforms = const [],
this.links = const [],
this.type,
required this.type,
this.date,
this.channel,
});

@ -12,9 +12,9 @@ Index _$IndexFromJson(Map json) {
$checkedConvert(
json,
'samples',
(v) => (v as List)
?.map((e) => e == null ? null : Sample.fromJson(e as Map))
?.toList()),
(v) => (v as List<dynamic>)
.map((e) => Sample.fromJson(e as Map))
.toList()),
);
return val;
});
@ -28,35 +28,35 @@ Sample _$SampleFromJson(Map json) {
return $checkedNew('Sample', json, () {
final val = Sample(
name: $checkedConvert(json, 'name', (v) => v as String),
author: $checkedConvert(json, 'author', (v) => v as String),
author: $checkedConvert(json, 'author', (v) => v as String?),
screenshots: $checkedConvert(
json,
'screenshots',
(v) => (v as List)
?.map((e) => e == null ? null : Screenshot.fromJson(e as Map))
?.toList()),
(v) => (v as List<dynamic>)
.map((e) => Screenshot.fromJson(e as Map))
.toList()),
source: $checkedConvert(json, 'source', (v) => v as String),
web: $checkedConvert(json, 'web', (v) => v as String),
web: $checkedConvert(json, 'web', (v) => v as String?),
description: $checkedConvert(json, 'description', (v) => v as String),
difficulty: $checkedConvert(json, 'difficulty', (v) => v as String),
difficulty: $checkedConvert(json, 'difficulty', (v) => v as String?),
widgets: $checkedConvert(json, 'widgets',
(v) => (v as List)?.map((e) => e as String)?.toList()),
(v) => (v as List<dynamic>).map((e) => e as String).toList()),
packages: $checkedConvert(json, 'packages',
(v) => (v as List)?.map((e) => e as String)?.toList()),
tags: $checkedConvert(
json, 'tags', (v) => (v as List)?.map((e) => e as String)?.toList()),
(v) => (v as List<dynamic>).map((e) => e as String).toList()),
tags: $checkedConvert(json, 'tags',
(v) => (v as List<dynamic>).map((e) => e as String).toList()),
platforms: $checkedConvert(json, 'platforms',
(v) => (v as List)?.map((e) => e as String)?.toList()),
(v) => (v as List<dynamic>).map((e) => e as String).toList()),
links: $checkedConvert(
json,
'links',
(v) => (v as List)
?.map((e) => e == null ? null : Link.fromJson(e as Map))
?.toList()),
(v) => (v as List<dynamic>)
.map((e) => Link.fromJson(e as Map))
.toList()),
type: $checkedConvert(json, 'type', (v) => v as String),
date: $checkedConvert(
json, 'date', (v) => v == null ? null : DateTime.parse(v as String)),
channel: $checkedConvert(json, 'channel', (v) => v as String),
channel: $checkedConvert(json, 'channel', (v) => v as String?),
);
return val;
});

@ -83,6 +83,7 @@ samples:
- scoped_model
tags: ['intermediate', 'sample', 'gallery', 'material', 'design', 'vignettes']
platforms: ['web', 'ios', 'android']
links: []
type: demo
- name: Add to App
@ -105,6 +106,7 @@ samples:
- provider
tags: ['advanced', 'sample', 'add-to-app', 'android', 'ios', 'native', 'embedding']
platforms: ['ios', 'android']
links: []
type: sample
- name: Animations
@ -133,6 +135,7 @@ samples:
- flutter/material
tags: ['intermediate', 'sample', 'animation']
platforms: ['ios', 'android', 'web']
links: []
type: sample
web: web/animations
@ -157,6 +160,7 @@ samples:
- google_maps_webservice
tags: ['intermediate', 'sample', 'firebase', 'maps']
platforms: ['ios', 'android']
links: []
type: sample
- name: Isolate Example
@ -181,6 +185,7 @@ samples:
- dart:math
tags: ['intermediate', 'sample', 'isolates', 'concurrency']
platforms: ['ios', 'android']
links: []
type: sample
- name: jsonexample
@ -209,6 +214,7 @@ samples:
- built_value_generator
tags: ['beginner', 'sample']
platforms: ['ios', 'android']
links: []
type: sample
- name: Place Tracker
@ -235,6 +241,7 @@ samples:
- google_maps_flutter
tags: ['intermediate', 'sample', 'json', 'serialization']
platforms: ['android']
links: []
type: sample
- name: Platform Design
@ -266,6 +273,7 @@ samples:
- flutter/cupertino
tags: ['advanced', 'sample', 'ios']
platforms: ['ios', 'android']
links: []
type: sample
- name: Platform View Swift
@ -287,6 +295,7 @@ samples:
- flutter/services
tags: ['advanced', 'sample', 'ios']
platforms: ['ios']
links: []
type: sample
- name: Infinite List
@ -311,6 +320,7 @@ samples:
- meta
tags: ['sample', 'material', 'design', 'android', 'ios']
platforms: ['ios', 'android']
links: []
type: sample
- name: IOS App Clip
@ -331,6 +341,7 @@ samples:
- device_info
tags: ['sample', 'Device Info', 'ios']
platforms: ['ios']
links: []
type: sample
- name: Testing App
@ -353,6 +364,7 @@ samples:
- provider
tags: ['sample', 'material', 'android', 'ios']
platforms: ['ios', 'android']
links: []
type: sample
- name: Provider Shopper
@ -376,6 +388,7 @@ samples:
- provider
tags: ['intermediate', 'sample', 'provider']
platforms: ['ios', 'android', 'web']
links: []
type: sample
web: web/provider_shopper
@ -402,6 +415,7 @@ samples:
- firebase
tags: ['intermediate', 'sample', 'firebase']
platforms: ['ios', 'android', 'web']
links: []
type: sample
web: web/web_dashboard
@ -423,6 +437,7 @@ samples:
packages: []
tags: ['intermediate', 'sample', 'forms']
platforms: ['ios', 'android', 'web']
links: []
type: sample
web: web/form_app
###################
@ -433,6 +448,7 @@ samples:
author: Flutter
screenshots:
- url: images/charts1.png
alt: Charts screenshot
source: https://github.com/google/charts
description: >
A general-purpose charting library.
@ -440,6 +456,7 @@ samples:
widgets: []
packages: []
platforms: ['ios', 'android', 'web']
links: []
tags: ['demo', 'charts']
web: web/charts
type: demo
@ -456,6 +473,7 @@ samples:
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'flutter create']
web: web/filipino_cuisine
type: demo
@ -472,6 +490,7 @@ samples:
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'data', 'visualization']
web: web/github_dataviz
type: demo
@ -488,6 +507,7 @@ samples:
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'animation']
web: web/particle_background
type: demo
@ -504,6 +524,7 @@ samples:
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'game']
web: web/slide_puzzle
type: demo
@ -520,6 +541,7 @@ samples:
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'animation']
web: https://z.flutter.gallery/#/dice
type: demo

@ -3,7 +3,7 @@
// found in the LICENSE file
bool matchesQuery(String query, String sampleAttributes) {
if (query == null || query.isEmpty) {
if (query.isEmpty) {
return true;
}
var queryWords = query.split(' ')..removeWhere((s) => s.isEmpty);
@ -68,11 +68,13 @@ String searchQueryFromParams(Map<String, String> params) {
}
if (params.containsKey('type')) {
if (buf.isNotEmpty) buf.write(' ');
buf.write('type:' + params['type']);
var value = params['type'];
if (value != null) buf.write('type:' + value);
}
if (params.containsKey('platform')) {
if (buf.isNotEmpty) buf.write(' ');
buf.write('platform:' + params['platform']);
var value = params['platform'];
if (value != null) buf.write('platform:' + value);
}
return buf.toString();

@ -187,7 +187,7 @@ String _descriptionPage(Sample sample) => '''
String _descriptionButtons(Sample sample) {
var buf = StringBuffer();
if (sample?.web?.isNotEmpty == true) {
if (sample.web?.isNotEmpty == true) {
buf.write(
'''<button class="mdc-button mdc-button--outlined" onclick="window.location.href = '${sample.web}';"><span class="mdc-button__ripple"></span> Launch App</button>''');
}

@ -23,7 +23,9 @@ String pascalCase(String input) {
String _fixCase(String input, String separator) =>
input.replaceAllMapped(_upperCase, (match) {
var lower = match.group(0).toLowerCase();
var group = match.group(0);
if (group == null) return input;
var lower = group.toLowerCase();
if (match.start > 0) {
lower = '$separator$lower';

@ -4,23 +4,29 @@ homepage: https://github.com/flutter/samples_index
author: Flutter Team <flutter-dev@googlegroups.com>
version: 0.0.1
environment:
sdk: ">=2.5.0 <3.0.0"
sdk: ">=2.12.0 <3.0.0"
dependencies:
json_annotation: ^3.0.0
json_annotation: ^4.0.1
path: ^1.6.0
yaml: ^2.2.0
mdc_web: ^0.5.0-pre
yaml: ^3.0.0
mdc_web: ^0.6.0
sass_builder: ^2.1.0
checked_yaml: ^1.0.0
webdriver: ^2.1.0
html: ^0.14.0
checked_yaml: ^2.0.1
webdriver: ^3.0.0
html: ^0.15.0
dev_dependencies:
grinder: ^0.8.3
grinder: ^0.9.0
flutter_lints: ^1.0.0
test: ^1.6.0
json_serializable: ^3.2.0
build: ^1.2.0
build_runner: ^1.7.0
build_web_compilers: ^2.7.0
tuneup: ^0.3.6
image: ^2.1.0
test: ^1.17.10
json_serializable: ^4.0.2
build: ^2.0.3
build_runner: ^2.0.6
build_web_compilers: ^3.0.0
tuneup: ^0.3.8
image: ^3.0.2
# waiting for the next material-components-web release.
# Once released, it will need to be rolled into package:mdc_web.
#
# https://github.com/material-components/material-components-web/pull/7158
dependency_overrides:
sass: 1.32.10

@ -16,15 +16,18 @@ void main() {
var contents = await file.readAsString();
expect(contents, isNotEmpty);
var index = checkedYamlDecode(contents, (m) => Index.fromJson(m),
var index = checkedYamlDecode(contents, (m) => m != null ? Index.fromJson(m) : null,
sourceUrl: file.uri);
if (index == null) {
throw('unable to load YAML from $file');
}
expect(index.samples, isNotEmpty);
var sample = index.samples.first;
expect(sample, isNotNull);
expect(sample.name, 'Kittens');
expect(sample.screenshots, hasLength(2));
expect(sample.source, 'http://github.com/johnpryan/kittens');
expect(sample.source, 'https://github.com/johnpryan/kittens');
expect(sample.description, 'A sample kitten app');
expect(sample.difficulty, 'beginner');
expect(sample.widgets, hasLength(2));
@ -36,7 +39,7 @@ void main() {
expect(sample.platforms, hasLength(3));
expect(sample.links, hasLength(2));
expect(sample.links[1].text, 'author');
expect(sample.links[1].href, 'http://jpryan.me');
expect(sample.links[1].href, 'https://jpryan.me');
expect(sample.type, 'sample');
expect(sample.date, DateTime.parse('2019-12-15T02:59:43.1Z'));
expect(sample.channel, 'stable');
@ -49,8 +52,11 @@ void main() {
var contents = await file.readAsString();
expect(contents, isNotEmpty);
var index = checkedYamlDecode(contents, (m) => Index.fromJson(m),
var index = checkedYamlDecode(contents, (m) => m != null ? Index.fromJson(m) : null,
sourceUrl: file.uri);
if (index == null) {
throw('unable to load YAML from $file');
}
var sample = index.samples.first;
expect(
sample.searchAttributes.split(' '),

@ -5,7 +5,7 @@ samples:
alt: a kitten
- url: https://placekitten.com/400/400
alt: another kitten
source: http://github.com/johnpryan/kittens
source: https://github.com/johnpryan/kittens
description: A sample kitten app
difficulty: beginner
widgets:
@ -20,7 +20,7 @@ samples:
- text: inspiration
href: https://apps.apple.com/us/app/neko-atsume-kitty-collector/id923917775
- text: author
href: http://jpryan.me
href: https://jpryan.me
type: sample # sample, app, or cookbook
date: 2019-12-15T02:59:43.1Z
channel: stable

@ -104,12 +104,11 @@ Future<void> _createThumbnails(Directory directory) async {
continue;
}
var file = entity as File;
var pathPrefix = path.dirname(file.path);
var pathPrefix = path.dirname(entity.path);
var thumbnailFile = File(path.join(pathPrefix, filename + '_thumb.png'));
var img = image.decodeImage(await file.readAsBytes());
var resized = image.copyResize(img, width: 640);
var img = image.decodeImage(await entity.readAsBytes());
var resized = image.copyResize(img!, width: 640);
filesToWrite.add(thumbnailFile.writeAsBytes(image.encodePng(resized)));
}

@ -3,8 +3,6 @@ import 'dart:html';
import 'package:mdc_web/mdc_web.dart';
import 'package:samples_index/src/carousel.dart';
InputElement searchInput;
void main() {
querySelectorAll('.mdc-card__primary-action').forEach((el) => MDCRipple(el));

@ -4,8 +4,8 @@ import 'package:mdc_web/mdc_web.dart';
import 'package:samples_index/browser.dart';
/// The Material text input for searching
MDCTextField searchBar;
MDCChipSet chipSet;
late final MDCTextField searchBar;
late final MDCChipSet chipSet;
/// The current set of query parameters that determine how the cards are
/// filtered. e.g. {'search': 'kittens', 'platform': 'ios'}
@ -16,9 +16,9 @@ const platformKey = 'platform';
void main() {
// Initialize Material components
MDCFloatingLabel(querySelector('.mdc-floating-label'));
searchBar = MDCTextField(querySelector('#search-bar'));
MDCRipple(querySelector('#clear-button'));
MDCFloatingLabel(querySelector('.mdc-floating-label')!);
searchBar = MDCTextField(querySelector('#search-bar')!);
MDCRipple(querySelector('#clear-button')!);
// Listen for hash changes
window.onHashChange.listen((_) {
@ -33,7 +33,7 @@ void main() {
querySelectorAll('.mdc-card__primary-action').forEach((el) => MDCRipple(el)
// Navigate to the description page when tapped
..listen('click', (e) {
window.location.href = el.attributes['href'];
window.location.href = el.attributes['href']!;
}));
// Filter cards on each keypress
@ -44,12 +44,12 @@ void main() {
// Update the URL only when the user is done typing in the search bar
searchBar.listen('change', (e) {
queryParams[searchKey] = searchBar.value;
queryParams[searchKey] = searchBar.value!;
updateHash();
});
// Update the hash, cards, and text input when the clear button is pressed
querySelector('#clear-button').onClick.listen((e) {
querySelector('#clear-button')!.onClick.listen((e) {
queryParams.remove('search');
updateHash();
setSearchBarText();
@ -57,10 +57,10 @@ void main() {
});
// Initialize chips
chipSet = MDCChipSet(querySelector('.mdc-chip-set'));
chipSet = MDCChipSet(querySelector('.mdc-chip-set')!);
chipSet.listen('MDCChip:selection', (e) {
// Get the query parameters for this chip
var selectedChipIndex = chipSet.chips.indexWhere((chip) => chip.selected);
var selectedChipIndex = chipSet.chips.indexWhere((chip) => chip.selected!);
var chipParams = paramsForChip(selectedChipIndex);
// Overwrite query parameters with new ones
@ -89,7 +89,7 @@ void setSearchBarText() {
void setSelectedChips() {
var type = queryParams.containsKey(typeKey) ? queryParams[typeKey] : '';
if (type.isNotEmpty) {
if (type!.isNotEmpty) {
if (type == 'sample') {
chipSet.chips[1].selected = true;
}
@ -101,7 +101,7 @@ void setSelectedChips() {
// Apply the platform from the hash in the URL
var platform =
queryParams.containsKey(platformKey) ? queryParams[platformKey] : '';
if (platform.isNotEmpty) {
if (platform!.isNotEmpty) {
if (platform == 'web') {
chipSet.chips[3].selected = true;
}
@ -113,7 +113,7 @@ void setSelectedChips() {
void handleSearch() {
var search = searchBar.value;
queryParams[searchKey] = search;
queryParams[searchKey] = search!;
filterCards();
}
@ -139,7 +139,7 @@ void filterCards() {
var elements = querySelectorAll('[search-attrs]');
for (var element in elements) {
var searchAttributes = element.attributes['search-attrs'];
if (matchesQuery(searchQuery, searchAttributes)) {
if (matchesQuery(searchQuery, searchAttributes!)) {
element.hidden = false;
} else {
element.hidden = true;

Loading…
Cancel
Save