mirror of
https://github.com/flutter/samples.git
synced 2025-11-11 07:18:15 +00:00
Update web/ samples to work with Flutter SDK (#134)
* add package:http dependency in dad_jokes * add package:http dependency in filipino_cuisine * don't build package:http demos until flutter/flutter#34858 is resolved * update gallery * update github_dataviz * update particle_background * don't build github_dataviz (uses package:http) * update slide_puzzle * update spinning_square * update timeflow * update vision_challenge * update charts * update dad_jokes * update filipino cuisine * ignore build output * update timeflow and vision_challenge * update slide_puzzle * don't commit build/ directory * move preview.png images to assets * fix path url join * update readme * update web/readme.md
This commit is contained in:
@@ -1,84 +1,101 @@
|
||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
import 'app_state.dart';
|
||||
import 'core/puzzle_animator.dart';
|
||||
import 'core/puzzle_proxy.dart';
|
||||
import 'flutter.dart';
|
||||
import 'puzzle_controls.dart';
|
||||
import 'puzzle_flow_delegate.dart';
|
||||
import 'frame_nanny.dart';
|
||||
import 'shared_theme.dart';
|
||||
import 'themes.dart';
|
||||
import 'value_tab_controller.dart';
|
||||
|
||||
class _PuzzleControls extends ChangeNotifier implements PuzzleControls {
|
||||
final PuzzleHomeState _parent;
|
||||
|
||||
_PuzzleControls(this._parent);
|
||||
|
||||
@override
|
||||
bool get autoPlay => _parent._autoPlay;
|
||||
|
||||
void _notify() => notifyListeners();
|
||||
|
||||
@override
|
||||
void Function(bool newValue) get setAutoPlayFunction {
|
||||
if (_parent.puzzle.solved) {
|
||||
return null;
|
||||
}
|
||||
return _parent._setAutoPlay;
|
||||
}
|
||||
|
||||
@override
|
||||
int get clickCount => _parent.puzzle.clickCount;
|
||||
|
||||
@override
|
||||
int get incorrectTiles => _parent.puzzle.incorrectTiles;
|
||||
|
||||
@override
|
||||
void reset() => _parent.puzzle.reset();
|
||||
}
|
||||
import 'theme_plaster.dart';
|
||||
import 'theme_seattle.dart';
|
||||
import 'theme_simple.dart';
|
||||
|
||||
class PuzzleHomeState extends State
|
||||
with SingleTickerProviderStateMixin, AppState {
|
||||
with TickerProviderStateMixin
|
||||
implements AppState {
|
||||
TabController _tabController;
|
||||
AnimationController _controller;
|
||||
Animation<Offset> _shuffleOffsetAnimation;
|
||||
|
||||
@override
|
||||
Animation<Offset> get shuffleOffsetAnimation => _shuffleOffsetAnimation;
|
||||
|
||||
@override
|
||||
final PuzzleAnimator puzzle;
|
||||
|
||||
@override
|
||||
final _AnimationNotifier animationNotifier = _AnimationNotifier();
|
||||
final animationNotifier = _AnimationNotifier();
|
||||
|
||||
@override
|
||||
TabController get tabController => _tabController;
|
||||
|
||||
final _nanny = FrameNanny();
|
||||
|
||||
SharedTheme _currentTheme;
|
||||
|
||||
@override
|
||||
SharedTheme get currentTheme => _currentTheme;
|
||||
|
||||
@override
|
||||
set currentTheme(SharedTheme theme) {
|
||||
setState(() {
|
||||
_currentTheme = theme;
|
||||
});
|
||||
}
|
||||
|
||||
Duration _tickerTimeSinceLastEvent = Duration.zero;
|
||||
Ticker _ticker;
|
||||
Duration _lastElapsed;
|
||||
StreamSubscription _puzzleEventSubscription;
|
||||
StreamSubscription sub;
|
||||
|
||||
bool _autoPlay = false;
|
||||
_PuzzleControls _autoPlayListenable;
|
||||
@override
|
||||
bool autoPlay = false;
|
||||
|
||||
PuzzleHomeState(this.puzzle) {
|
||||
_puzzleEventSubscription = puzzle.onEvent.listen(_onPuzzleEvent);
|
||||
sub = puzzle.onEvent.listen(_onPuzzleEvent);
|
||||
|
||||
_themeDataCache = List.unmodifiable([
|
||||
ThemeSimple(this),
|
||||
ThemeSeattle(this),
|
||||
ThemePlaster(this),
|
||||
]);
|
||||
|
||||
_currentTheme = themeData.first;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_autoPlayListenable = _PuzzleControls(this);
|
||||
_ticker ??= createTicker(_onTick);
|
||||
_ensureTicking();
|
||||
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
|
||||
_shuffleOffsetAnimation = _controller.drive(const _Shake());
|
||||
_tabController = TabController(vsync: this, length: _themeDataCache.length);
|
||||
|
||||
_tabController.addListener(() {
|
||||
currentTheme = _themeDataCache[_tabController.index];
|
||||
});
|
||||
}
|
||||
|
||||
void _setAutoPlay(bool newValue) {
|
||||
if (newValue != _autoPlay) {
|
||||
List<SharedTheme> _themeDataCache;
|
||||
|
||||
@override
|
||||
Iterable<SharedTheme> get themeData => _themeDataCache;
|
||||
|
||||
@override
|
||||
void setAutoPlay(bool newValue) {
|
||||
if (newValue != autoPlay) {
|
||||
setState(() {
|
||||
// Only allow enabling autoPlay if the puzzle is not solved
|
||||
_autoPlayListenable._notify();
|
||||
_autoPlay = newValue && !puzzle.solved;
|
||||
if (_autoPlay) {
|
||||
autoPlay = newValue && !puzzle.solved;
|
||||
if (autoPlay) {
|
||||
_ensureTicking();
|
||||
}
|
||||
});
|
||||
@@ -86,46 +103,26 @@ class PuzzleHomeState extends State
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => MultiProvider(
|
||||
providers: [
|
||||
Provider<AppState>.value(value: this),
|
||||
ListenableProvider<PuzzleControls>.value(
|
||||
listenable: _autoPlayListenable,
|
||||
)
|
||||
],
|
||||
child: Material(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
const SizedBox.expand(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: Image(
|
||||
image: AssetImage('seattle.jpg'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const LayoutBuilder(builder: _doBuild),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
Widget build(BuildContext context) => _currentTheme.build(context);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
animationNotifier.dispose();
|
||||
_tabController.dispose();
|
||||
_controller?.dispose();
|
||||
_ticker?.dispose();
|
||||
_autoPlayListenable?.dispose();
|
||||
_puzzleEventSubscription.cancel();
|
||||
sub.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onPuzzleEvent(PuzzleEvent e) {
|
||||
_autoPlayListenable._notify();
|
||||
if (e != PuzzleEvent.random) {
|
||||
_setAutoPlay(false);
|
||||
}
|
||||
_tickerTimeSinceLastEvent = Duration.zero;
|
||||
_ensureTicking();
|
||||
if (e == PuzzleEvent.noop) {
|
||||
assert(e == PuzzleEvent.noop);
|
||||
_controller.reset();
|
||||
_controller.forward();
|
||||
}
|
||||
setState(() {
|
||||
// noop
|
||||
});
|
||||
@@ -151,141 +148,40 @@ class PuzzleHomeState extends State
|
||||
}
|
||||
|
||||
_tickerTimeSinceLastEvent += delta;
|
||||
puzzle.update(delta > _maxFrameDuration ? _maxFrameDuration : delta);
|
||||
puzzle.update(_nanny.tick(delta));
|
||||
|
||||
if (!puzzle.stable) {
|
||||
animationNotifier.animate();
|
||||
} else {
|
||||
if (!_autoPlay) {
|
||||
if (!autoPlay) {
|
||||
_ticker.stop();
|
||||
_lastElapsed = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_autoPlay &&
|
||||
if (autoPlay &&
|
||||
_tickerTimeSinceLastEvent > const Duration(milliseconds: 200)) {
|
||||
puzzle.playRandom();
|
||||
|
||||
if (puzzle.solved) {
|
||||
_setAutoPlay(false);
|
||||
setAutoPlay(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimationNotifier extends ChangeNotifier {
|
||||
class _Shake extends Animatable<Offset> {
|
||||
const _Shake();
|
||||
|
||||
@override
|
||||
Offset transform(double t) => Offset(0.01 * math.sin(t * math.pi * 3), 0);
|
||||
}
|
||||
|
||||
class _AnimationNotifier extends ChangeNotifier implements AnimationNotifier {
|
||||
_AnimationNotifier();
|
||||
|
||||
@override
|
||||
void animate() {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
const _maxFrameDuration = Duration(milliseconds: 34);
|
||||
|
||||
Widget _updateConstraints(
|
||||
BoxConstraints constraints, Widget Function(bool small) builder) {
|
||||
const _smallWidth = 580;
|
||||
|
||||
final constraintWidth =
|
||||
constraints.hasBoundedWidth ? constraints.maxWidth : 1000.0;
|
||||
|
||||
final constraintHeight =
|
||||
constraints.hasBoundedHeight ? constraints.maxHeight : 1000.0;
|
||||
|
||||
return builder(constraintWidth < _smallWidth || constraintHeight < 690);
|
||||
}
|
||||
|
||||
Widget _doBuild(BuildContext _, BoxConstraints constraints) =>
|
||||
_updateConstraints(constraints, _doBuildCore);
|
||||
|
||||
Widget _doBuildCore(bool small) => ValueTabController<SharedTheme>(
|
||||
values: themes,
|
||||
child: Consumer<SharedTheme>(
|
||||
builder: (_, theme, __) => AnimatedContainer(
|
||||
duration: puzzleAnimationDuration,
|
||||
color: theme.puzzleThemeBackground,
|
||||
child: Center(
|
||||
child: theme.styledWrapper(
|
||||
small,
|
||||
SizedBox(
|
||||
width: 580,
|
||||
child: Consumer<AppState>(
|
||||
builder: (context, appState, _) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Colors.black26,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
margin:
|
||||
const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: TabBar(
|
||||
controller: ValueTabController.of(context),
|
||||
labelPadding:
|
||||
const EdgeInsets.fromLTRB(0, 20, 0, 12),
|
||||
labelColor: theme.puzzleAccentColor,
|
||||
indicatorColor: theme.puzzleAccentColor,
|
||||
indicatorWeight: 1.5,
|
||||
unselectedLabelColor:
|
||||
Colors.black.withOpacity(0.6),
|
||||
tabs: themes
|
||||
.map((st) => Text(
|
||||
st.name.toUpperCase(),
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Flow(
|
||||
delegate: PuzzleFlowDelegate(
|
||||
small
|
||||
? const Size(90, 90)
|
||||
: const Size(140, 140),
|
||||
appState.puzzle,
|
||||
appState.animationNotifier,
|
||||
),
|
||||
children: List<Widget>.generate(
|
||||
appState.puzzle.length,
|
||||
(i) => theme.tileButtonCore(
|
||||
i, appState.puzzle, small),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Colors.black26, width: 1),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
bottom: 6,
|
||||
top: 2,
|
||||
right: 10,
|
||||
),
|
||||
child: Consumer<PuzzleControls>(
|
||||
builder: (_, controls, __) => Row(
|
||||
children: theme.bottomControls(controls)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user