mirror of
https://github.com/flutter/samples.git
synced 2025-11-10 06:48:26 +00:00
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
This commit is contained in:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user