mirror of https://github.com/flutter/samples.git
parent
0a88d4519e
commit
a1998e6186
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 Tim Sneath
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,4 +0,0 @@
|
|||||||
A fun little app for groan-worthy dad jokes.
|
|
||||||
|
|
||||||
Original source at
|
|
||||||
[github.com/timsneath/dadjokes](https://github.com/timsneath/dadjokes/).
|
|
@ -1,93 +0,0 @@
|
|||||||
include: package:pedantic/analysis_options.1.8.0.yaml
|
|
||||||
analyzer:
|
|
||||||
strong-mode:
|
|
||||||
implicit-casts: false
|
|
||||||
linter:
|
|
||||||
rules:
|
|
||||||
- always_declare_return_types
|
|
||||||
- annotate_overrides
|
|
||||||
- avoid_bool_literals_in_conditional_expressions
|
|
||||||
- avoid_classes_with_only_static_members
|
|
||||||
- avoid_empty_else
|
|
||||||
- avoid_function_literals_in_foreach_calls
|
|
||||||
- avoid_init_to_null
|
|
||||||
- avoid_null_checks_in_equality_operators
|
|
||||||
- avoid_relative_lib_imports
|
|
||||||
- avoid_renaming_method_parameters
|
|
||||||
- avoid_return_types_on_setters
|
|
||||||
- avoid_returning_null
|
|
||||||
- avoid_returning_null_for_future
|
|
||||||
- avoid_returning_null_for_void
|
|
||||||
- avoid_returning_this
|
|
||||||
- avoid_shadowing_type_parameters
|
|
||||||
- avoid_single_cascade_in_expression_statements
|
|
||||||
- avoid_types_as_parameter_names
|
|
||||||
- avoid_unused_constructor_parameters
|
|
||||||
- await_only_futures
|
|
||||||
- camel_case_types
|
|
||||||
- cancel_subscriptions
|
|
||||||
- cascade_invocations
|
|
||||||
- comment_references
|
|
||||||
- constant_identifier_names
|
|
||||||
- control_flow_in_finally
|
|
||||||
- directives_ordering
|
|
||||||
- empty_catches
|
|
||||||
- empty_constructor_bodies
|
|
||||||
- empty_statements
|
|
||||||
- file_names
|
|
||||||
- hash_and_equals
|
|
||||||
- implementation_imports
|
|
||||||
- invariant_booleans
|
|
||||||
- iterable_contains_unrelated_type
|
|
||||||
- join_return_with_assignment
|
|
||||||
- library_names
|
|
||||||
- library_prefixes
|
|
||||||
- list_remove_unrelated_type
|
|
||||||
- literal_only_boolean_expressions
|
|
||||||
- no_adjacent_strings_in_list
|
|
||||||
- no_duplicate_case_values
|
|
||||||
- non_constant_identifier_names
|
|
||||||
- null_closures
|
|
||||||
- omit_local_variable_types
|
|
||||||
- only_throw_errors
|
|
||||||
- overridden_fields
|
|
||||||
- package_api_docs
|
|
||||||
- package_names
|
|
||||||
- package_prefixed_library_names
|
|
||||||
- prefer_adjacent_string_concatenation
|
|
||||||
- prefer_collection_literals
|
|
||||||
- prefer_conditional_assignment
|
|
||||||
- prefer_const_constructors
|
|
||||||
- prefer_contains
|
|
||||||
- prefer_equal_for_default_values
|
|
||||||
- prefer_final_fields
|
|
||||||
- prefer_final_locals
|
|
||||||
- prefer_generic_function_type_aliases
|
|
||||||
- prefer_initializing_formals
|
|
||||||
- prefer_interpolation_to_compose_strings
|
|
||||||
- prefer_is_empty
|
|
||||||
- prefer_is_not_empty
|
|
||||||
- prefer_null_aware_operators
|
|
||||||
- prefer_single_quotes
|
|
||||||
- prefer_typing_uninitialized_variables
|
|
||||||
- recursive_getters
|
|
||||||
- slash_for_doc_comments
|
|
||||||
- test_types_in_equals
|
|
||||||
- throw_in_finally
|
|
||||||
- type_init_formals
|
|
||||||
- unawaited_futures
|
|
||||||
- unnecessary_await_in_return
|
|
||||||
- unnecessary_brace_in_string_interps
|
|
||||||
- unnecessary_const
|
|
||||||
- unnecessary_getters_setters
|
|
||||||
- unnecessary_lambdas
|
|
||||||
- unnecessary_new
|
|
||||||
- unnecessary_null_aware_assignments
|
|
||||||
- unnecessary_parenthesis
|
|
||||||
- unnecessary_statements
|
|
||||||
- unnecessary_this
|
|
||||||
- unrelated_type_equality_checks
|
|
||||||
- use_function_type_syntax_for_parameters
|
|
||||||
- use_rethrow_when_possible
|
|
||||||
- valid_regexps
|
|
||||||
- void_checks
|
|
@ -1,18 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"family": "MaterialIcons",
|
|
||||||
"fonts": [
|
|
||||||
{
|
|
||||||
"asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"family": "Patrick Hand",
|
|
||||||
"fonts": [
|
|
||||||
{
|
|
||||||
"asset": "fonts/PatrickHand-Regular.ttf"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
Binary file not shown.
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 7.5 KiB |
@ -1,404 +0,0 @@
|
|||||||
// Package auto_size_text:
|
|
||||||
// https://pub.dartlang.org/packages/auto_size_text
|
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
|
|
||||||
bool checkTextFits(TextSpan text, Locale locale, double scale, int maxLines,
|
|
||||||
double maxWidth, double maxHeight) {
|
|
||||||
final tp = TextPainter(
|
|
||||||
text: text,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
textScaleFactor: scale ?? 1,
|
|
||||||
maxLines: maxLines,
|
|
||||||
locale: locale,
|
|
||||||
)..layout(maxWidth: maxWidth);
|
|
||||||
|
|
||||||
return !(tp.didExceedMaxLines ||
|
|
||||||
tp.height > maxHeight ||
|
|
||||||
tp.width > maxWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flutter widget that automatically resizes text to fit perfectly within its bounds.
|
|
||||||
///
|
|
||||||
/// All size constraints as well as maxLines are taken into account. If the text
|
|
||||||
/// overflows anyway, you should check if the parent widget actually constraints
|
|
||||||
/// the size of this widget.
|
|
||||||
class AutoSizeText extends StatefulWidget {
|
|
||||||
/// Creates a [AutoSizeText] widget.
|
|
||||||
///
|
|
||||||
/// If the [style] argument is null, the text will use the style from the
|
|
||||||
/// closest enclosing [DefaultTextStyle].
|
|
||||||
const AutoSizeText(
|
|
||||||
this.data, {
|
|
||||||
Key key,
|
|
||||||
this.style,
|
|
||||||
this.minFontSize = 12.0,
|
|
||||||
this.maxFontSize,
|
|
||||||
this.stepGranularity = 1.0,
|
|
||||||
this.presetFontSizes,
|
|
||||||
this.group,
|
|
||||||
this.textAlign,
|
|
||||||
this.textDirection,
|
|
||||||
this.locale,
|
|
||||||
this.softWrap,
|
|
||||||
this.overflow,
|
|
||||||
this.textScaleFactor,
|
|
||||||
this.maxLines,
|
|
||||||
this.semanticsLabel,
|
|
||||||
}) : assert(data != null),
|
|
||||||
assert(stepGranularity >= 0.1),
|
|
||||||
textSpan = null,
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
/// Creates a [AutoSizeText] widget with a [TextSpan].
|
|
||||||
const AutoSizeText.rich(
|
|
||||||
this.textSpan, {
|
|
||||||
Key key,
|
|
||||||
this.style,
|
|
||||||
this.minFontSize = 12.0,
|
|
||||||
this.maxFontSize,
|
|
||||||
this.stepGranularity = 1.0,
|
|
||||||
this.presetFontSizes,
|
|
||||||
this.group,
|
|
||||||
this.textAlign,
|
|
||||||
this.textDirection,
|
|
||||||
this.locale,
|
|
||||||
this.softWrap,
|
|
||||||
this.overflow,
|
|
||||||
this.textScaleFactor,
|
|
||||||
this.maxLines,
|
|
||||||
this.semanticsLabel,
|
|
||||||
}) : assert(textSpan != null),
|
|
||||||
assert(stepGranularity >= 0.1),
|
|
||||||
data = null,
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
/// The text to display.
|
|
||||||
///
|
|
||||||
/// This will be null if a [textSpan] is provided instead.
|
|
||||||
final String data;
|
|
||||||
|
|
||||||
/// The text to display as a [TextSpan].
|
|
||||||
///
|
|
||||||
/// This will be null if [data] is provided instead.
|
|
||||||
final TextSpan textSpan;
|
|
||||||
|
|
||||||
/// If non-null, the style to use for this text.
|
|
||||||
///
|
|
||||||
/// If the style's "inherit" property is true, the style will be merged with
|
|
||||||
/// the closest enclosing [DefaultTextStyle]. Otherwise, the style will
|
|
||||||
/// replace the closest enclosing [DefaultTextStyle].
|
|
||||||
final TextStyle style;
|
|
||||||
|
|
||||||
/// The minimum text size constraint to be used when auto-sizing text.
|
|
||||||
///
|
|
||||||
/// Is being ignored if [presetFontSizes] is set.
|
|
||||||
final double minFontSize;
|
|
||||||
|
|
||||||
/// The maximum text size constraint to be used when auto-sizing text.
|
|
||||||
///
|
|
||||||
/// Is being ignored if [presetFontSizes] is set.
|
|
||||||
final double maxFontSize;
|
|
||||||
|
|
||||||
/// The steps in which the font size is being adapted to constraints.
|
|
||||||
///
|
|
||||||
/// The Text scales uniformly in a range between [minFontSize] and
|
|
||||||
/// [maxFontSize].
|
|
||||||
/// Each increment occurs as per the step size set in stepGranularity.
|
|
||||||
///
|
|
||||||
/// Most of the time you don't want a stepGranularity below 1.0.
|
|
||||||
///
|
|
||||||
/// Is being ignored if [presetFontSizes] is set.
|
|
||||||
final double stepGranularity;
|
|
||||||
|
|
||||||
/// Lets you specify all the possible font sizes.
|
|
||||||
///
|
|
||||||
/// **Important:** The presetFontSizes are used the order they are given in.
|
|
||||||
/// If the first fontSize matches, all others are being ignored.
|
|
||||||
final List<double> presetFontSizes;
|
|
||||||
|
|
||||||
/// Synchronizes the size of multiple [AutoSizeText]s.
|
|
||||||
///
|
|
||||||
/// If you want multiple [AutoSizeText]s to have the same text size, give all
|
|
||||||
/// of them the same [AutoSizeGroup] instance. All of them will have the
|
|
||||||
/// size of the smallest [AutoSizeText]
|
|
||||||
final AutoSizeGroup group;
|
|
||||||
|
|
||||||
/// How the text should be aligned horizontally.
|
|
||||||
final TextAlign textAlign;
|
|
||||||
|
|
||||||
/// The directionality of the text.
|
|
||||||
///
|
|
||||||
/// This decides how [textAlign] values like [TextAlign.start] and
|
|
||||||
/// [TextAlign.end] are interpreted.
|
|
||||||
///
|
|
||||||
/// This is also used to disambiguate how to render bidirectional text. For
|
|
||||||
/// example, if the [data] is an English phrase followed by a Hebrew phrase,
|
|
||||||
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
|
||||||
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
|
||||||
/// context, the English phrase will be on the right and the Hebrew phrase on
|
|
||||||
/// its left.
|
|
||||||
///
|
|
||||||
/// Defaults to the ambient [Directionality], if any.
|
|
||||||
final TextDirection textDirection;
|
|
||||||
|
|
||||||
/// Used to select a font when the same Unicode character can
|
|
||||||
/// be rendered differently, depending on the locale.
|
|
||||||
///
|
|
||||||
/// It's rarely necessary to set this property. By default its value
|
|
||||||
/// is inherited from the enclosing app with `Localizations.localeOf(context)`.
|
|
||||||
final Locale locale;
|
|
||||||
|
|
||||||
/// Whether the text should break at soft line breaks.
|
|
||||||
///
|
|
||||||
/// If false, the glyphs in the text will be positioned as if there was
|
|
||||||
/// unlimited horizontal space.
|
|
||||||
final bool softWrap;
|
|
||||||
|
|
||||||
/// How visual overflow should be handled.
|
|
||||||
final TextOverflow overflow;
|
|
||||||
|
|
||||||
/// The number of font pixels for each logical pixel.
|
|
||||||
///
|
|
||||||
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
|
||||||
/// the specified font size.
|
|
||||||
///
|
|
||||||
/// This property also affects [minFontSize], [maxFontSize] and [presetFontSizes].
|
|
||||||
///
|
|
||||||
/// The value given to the constructor as textScaleFactor. If null, will
|
|
||||||
/// use the [MediaQueryData.textScaleFactor] obtained from the ambient
|
|
||||||
/// [MediaQuery], or 1.0 if there is no [MediaQuery] in scope.
|
|
||||||
final double textScaleFactor;
|
|
||||||
|
|
||||||
/// An optional maximum number of lines for the text to span, wrapping if necessary.
|
|
||||||
/// If the text exceeds the given number of lines, it will be resized according
|
|
||||||
/// to the specified bounds and if necessary truncated according to [overflow].
|
|
||||||
///
|
|
||||||
/// If this is 1, text will not wrap. Otherwise, text will be wrapped at the
|
|
||||||
/// edge of the box.
|
|
||||||
///
|
|
||||||
/// If this is null, but there is an ambient [DefaultTextStyle] that specifies
|
|
||||||
/// an explicit number for its [DefaultTextStyle.maxLines], then the
|
|
||||||
/// [DefaultTextStyle] value will take precedence. You can use a [RichText]
|
|
||||||
/// widget directly to entirely override the [DefaultTextStyle].
|
|
||||||
final int maxLines;
|
|
||||||
|
|
||||||
/// An alternative semantics label for this text.
|
|
||||||
///
|
|
||||||
/// If present, the semantics of this widget will contain this value instead
|
|
||||||
/// of the actual text.
|
|
||||||
final String semanticsLabel;
|
|
||||||
|
|
||||||
@override
|
|
||||||
_AutoSizeTextState createState() => _AutoSizeTextState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AutoSizeTextState extends State<AutoSizeText> {
|
|
||||||
double _previousFontSize;
|
|
||||||
|
|
||||||
Text _cachedText;
|
|
||||||
double _cachedFontSize;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
if (widget.group != null) {
|
|
||||||
widget.group._register(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(AutoSizeText oldWidget) {
|
|
||||||
_cachedText = null;
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return LayoutBuilder(builder: (context, size) {
|
|
||||||
final defaultTextStyle = DefaultTextStyle.of(context);
|
|
||||||
|
|
||||||
var style = widget.style;
|
|
||||||
if (widget.style == null || widget.style.inherit) {
|
|
||||||
style = defaultTextStyle.style.merge(widget.style);
|
|
||||||
}
|
|
||||||
|
|
||||||
final fontSize = _calculateFontSize(size, style, defaultTextStyle);
|
|
||||||
|
|
||||||
Widget text;
|
|
||||||
|
|
||||||
if (widget.group != null) {
|
|
||||||
if (fontSize != _previousFontSize) {
|
|
||||||
widget.group._updateFontSize(this, fontSize);
|
|
||||||
}
|
|
||||||
text = _buildText(widget.group._fontSize, style);
|
|
||||||
} else {
|
|
||||||
text = _buildText(fontSize, style);
|
|
||||||
}
|
|
||||||
|
|
||||||
_previousFontSize = fontSize;
|
|
||||||
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
double _calculateFontSize(
|
|
||||||
BoxConstraints size, TextStyle style, DefaultTextStyle defaultStyle) {
|
|
||||||
final userScale =
|
|
||||||
widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context);
|
|
||||||
|
|
||||||
final minFontSize = widget.minFontSize ?? 0;
|
|
||||||
assert(
|
|
||||||
minFontSize >= 0, 'MinFontSize has to be greater than or equal to 0.');
|
|
||||||
|
|
||||||
final maxFontSize = widget.maxFontSize ?? double.infinity;
|
|
||||||
assert(maxFontSize > 0, 'MaxFontSize has to be greater than 0.');
|
|
||||||
|
|
||||||
assert(minFontSize <= maxFontSize,
|
|
||||||
'MinFontSize has to be smaller or equal than maxFontSize.');
|
|
||||||
|
|
||||||
final maxLines = widget.maxLines ?? defaultStyle.maxLines;
|
|
||||||
|
|
||||||
var presetIndex = 0;
|
|
||||||
if (widget.presetFontSizes != null) {
|
|
||||||
assert(widget.presetFontSizes.isNotEmpty, 'PresetFontSizes is empty.');
|
|
||||||
}
|
|
||||||
|
|
||||||
double initialFontSize;
|
|
||||||
if (widget.presetFontSizes == null) {
|
|
||||||
final current = style.fontSize;
|
|
||||||
initialFontSize = current.clamp(minFontSize, maxFontSize).toDouble();
|
|
||||||
} else {
|
|
||||||
initialFontSize = widget.presetFontSizes[presetIndex++];
|
|
||||||
}
|
|
||||||
|
|
||||||
var fontSize = initialFontSize * userScale;
|
|
||||||
|
|
||||||
final span = TextSpan(
|
|
||||||
style: widget.textSpan?.style ?? style,
|
|
||||||
text: widget.textSpan?.text ?? widget.data,
|
|
||||||
children: widget.textSpan?.children,
|
|
||||||
recognizer: widget.textSpan?.recognizer,
|
|
||||||
);
|
|
||||||
while (!checkTextFits(span, widget.locale, fontSize / style.fontSize,
|
|
||||||
maxLines, size.maxWidth, size.maxHeight)) {
|
|
||||||
if (widget.presetFontSizes == null) {
|
|
||||||
final newFontSize = fontSize - widget.stepGranularity;
|
|
||||||
if (newFontSize < (minFontSize * userScale)) break;
|
|
||||||
fontSize = newFontSize;
|
|
||||||
} else if (presetIndex < widget.presetFontSizes.length) {
|
|
||||||
fontSize = widget.presetFontSizes[presetIndex++] * userScale;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildText(double fontSize, TextStyle style) {
|
|
||||||
if (_cachedText != null && _cachedFontSize == fontSize) {
|
|
||||||
return _cachedText;
|
|
||||||
}
|
|
||||||
|
|
||||||
Text text;
|
|
||||||
if (widget.data != null) {
|
|
||||||
text = Text(
|
|
||||||
widget.data,
|
|
||||||
style: style.copyWith(fontSize: fontSize),
|
|
||||||
textAlign: widget.textAlign,
|
|
||||||
textDirection: widget.textDirection,
|
|
||||||
locale: widget.locale,
|
|
||||||
softWrap: widget.softWrap,
|
|
||||||
overflow: widget.overflow,
|
|
||||||
textScaleFactor: 1,
|
|
||||||
maxLines: widget.maxLines,
|
|
||||||
semanticsLabel: widget.semanticsLabel,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
text = Text.rich(
|
|
||||||
widget.textSpan,
|
|
||||||
style: style,
|
|
||||||
textAlign: widget.textAlign,
|
|
||||||
textDirection: widget.textDirection,
|
|
||||||
locale: widget.locale,
|
|
||||||
softWrap: widget.softWrap,
|
|
||||||
overflow: widget.overflow,
|
|
||||||
textScaleFactor: fontSize / style.fontSize,
|
|
||||||
maxLines: widget.maxLines,
|
|
||||||
semanticsLabel: widget.semanticsLabel,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_cachedText = text;
|
|
||||||
_cachedFontSize = fontSize;
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notifySync() {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
if (widget.group != null) {
|
|
||||||
widget.group._remove(this);
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AutoSizeGroup {
|
|
||||||
final _listeners = <_AutoSizeTextState, double>{};
|
|
||||||
var _widgetsNotified = false;
|
|
||||||
double _fontSize = double.infinity;
|
|
||||||
|
|
||||||
void _register(_AutoSizeTextState text) {
|
|
||||||
_listeners[text] = double.infinity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateFontSize(_AutoSizeTextState text, double maxFontSize) {
|
|
||||||
final oldFontSize = _fontSize;
|
|
||||||
if (maxFontSize <= _fontSize) {
|
|
||||||
_fontSize = maxFontSize;
|
|
||||||
_listeners[text] = maxFontSize;
|
|
||||||
} else if (_listeners[text] == _fontSize) {
|
|
||||||
_listeners[text] = maxFontSize;
|
|
||||||
_fontSize = double.infinity;
|
|
||||||
for (var size in _listeners.values) {
|
|
||||||
if (size < _fontSize) _fontSize = size;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_listeners[text] = maxFontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldFontSize != _fontSize) {
|
|
||||||
_widgetsNotified = false;
|
|
||||||
// Timer.run(_notifyListeners);
|
|
||||||
_notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notifyListeners() {
|
|
||||||
if (_widgetsNotified) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
_widgetsNotified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var text in _listeners.keys.toList()) {
|
|
||||||
if (text.mounted) {
|
|
||||||
text._notifySync();
|
|
||||||
} else {
|
|
||||||
_listeners.remove(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _remove(_AutoSizeTextState text) {
|
|
||||||
_updateFontSize(text, double.infinity);
|
|
||||||
_listeners.remove(text);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'main_page.dart';
|
|
||||||
|
|
||||||
void main() => runApp(MyApp());
|
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Dad Jokes',
|
|
||||||
theme: ThemeData(
|
|
||||||
primarySwatch: Colors.deepOrange,
|
|
||||||
),
|
|
||||||
home: MainPage(title: 'Dad Jokes'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
import 'auto_size_text.dart';
|
|
||||||
|
|
||||||
const _dadJokeApi = 'https://icanhazdadjoke.com/';
|
|
||||||
const _httpHeaders = {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
};
|
|
||||||
|
|
||||||
const jokeTextStyle = TextStyle(
|
|
||||||
fontFamily: 'Patrick Hand',
|
|
||||||
fontSize: 36,
|
|
||||||
fontStyle: FontStyle.normal,
|
|
||||||
fontWeight: FontWeight.normal);
|
|
||||||
|
|
||||||
class MainPage extends StatefulWidget {
|
|
||||||
MainPage({Key key, this.title}) : super(key: key);
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
MainPageState createState() => MainPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class MainPageState extends State<MainPage> {
|
|
||||||
Future<String> _response;
|
|
||||||
String _displayedJoke = '';
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_refreshAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _refreshAction() {
|
|
||||||
setState(() {
|
|
||||||
_response = http.read(_dadJokeApi, headers: _httpHeaders);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _aboutAction() {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const AlertDialog(
|
|
||||||
title: Text('About Dad Jokes'),
|
|
||||||
content: Text(
|
|
||||||
'Dad jokes is brought to you by Tim Sneath (@timsneath), '
|
|
||||||
'proud dad of Naomi, Esther, and Silas. May your children '
|
|
||||||
'groan like mine will.\n\nDad jokes come from '
|
|
||||||
'https://icanhazdadjoke.com with thanks.'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureBuilder<String> _jokeBody() {
|
|
||||||
return FutureBuilder<String>(
|
|
||||||
future: _response,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
|
||||||
switch (snapshot.connectionState) {
|
|
||||||
case ConnectionState.none:
|
|
||||||
return const ListTile(
|
|
||||||
leading: Icon(Icons.sync_problem),
|
|
||||||
title: Text('No connection'),
|
|
||||||
);
|
|
||||||
case ConnectionState.waiting:
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
default:
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
return const Center(
|
|
||||||
child: ListTile(
|
|
||||||
leading: Icon(Icons.error),
|
|
||||||
title: Text('Network error'),
|
|
||||||
subtitle: Text(
|
|
||||||
'Sorry - this isn\'t funny, we know, but something went '
|
|
||||||
'wrong when connecting to the Internet. Check your '
|
|
||||||
'network connection and try again.'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final decoded = json.decode(snapshot.data);
|
|
||||||
if (decoded['status'] == 200) {
|
|
||||||
_displayedJoke = decoded['joke'] as String;
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Dismissible(
|
|
||||||
key: const Key('joke'),
|
|
||||||
direction: DismissDirection.horizontal,
|
|
||||||
onDismissed: (direction) {
|
|
||||||
_refreshAction();
|
|
||||||
},
|
|
||||||
child: AutoSizeText(_displayedJoke, style: jokeTextStyle),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return ListTile(
|
|
||||||
leading: const Icon(Icons.sync_problem),
|
|
||||||
title: Text('Unexpected error: ${snapshot.data}'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
leading: Image.asset(
|
|
||||||
'icon.png',
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
),
|
|
||||||
title: Text(widget.title),
|
|
||||||
actions: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.info),
|
|
||||||
tooltip: 'About Dad Jokes',
|
|
||||||
onPressed: _aboutAction,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: SafeArea(child: _jokeBody()),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
|
||||||
onPressed: _refreshAction,
|
|
||||||
icon: const Icon(Icons.mood),
|
|
||||||
label: const Text('NEW JOKE'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
# Generated by pub
|
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
|
||||||
packages:
|
|
||||||
async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: async
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.0"
|
|
||||||
charcode:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: charcode
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.2"
|
|
||||||
collection:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: collection
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.14.11"
|
|
||||||
flutter:
|
|
||||||
dependency: "direct main"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
http:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.12.0+4"
|
|
||||||
http_parser:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http_parser
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.3"
|
|
||||||
meta:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: meta
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.8"
|
|
||||||
path:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.6.4"
|
|
||||||
pedantic:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: pedantic
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.9.0"
|
|
||||||
sky_engine:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.99"
|
|
||||||
source_span:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: source_span
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.6.0"
|
|
||||||
string_scanner:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: string_scanner
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.5"
|
|
||||||
term_glyph:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: term_glyph
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
typed_data:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: typed_data
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.6"
|
|
||||||
vector_math:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_math
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.8"
|
|
||||||
sdks:
|
|
||||||
dart: ">=2.6.0 <3.0.0"
|
|
@ -1,18 +0,0 @@
|
|||||||
name: dad_jokes
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=2.2.0 <3.0.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
http: ^0.12.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
pedantic: ^1.8.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- icon.png
|
|
||||||
- preview.png
|
|
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title></title>
|
|
||||||
<script defer src="main.dart.js" type="application/javascript"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in new issue