Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b8dfeb2
Stub out buttons with corresponding Styles
GroovinChip Apr 9, 2021
875bae2
Stub out Fields and Labels with corresponding Styles
GroovinChip Apr 9, 2021
f93f8e9
Stub out Selectors
GroovinChip Apr 9, 2021
93a9ad0
Placeholder Indicators
GroovinChip Apr 9, 2021
8e89219
Merge branch 'dev' into stubs
GroovinChip Apr 13, 2021
71f9717
Remove stubs for RoundButton and BevelButton
GroovinChip Apr 13, 2021
e770403
Start working on PushButton
GroovinChip Apr 13, 2021
b943d27
Merge updates from dev
GroovinChip Apr 13, 2021
8800ec1
Get PushButton color from style's primaryColor instead of PushButton'…
GroovinChip Apr 13, 2021
9f8e8ef
Tweak PushButton's disabledColor
GroovinChip Apr 13, 2021
f9c22f3
Start work on PushButtonStyle
GroovinChip Apr 13, 2021
a0704ea
Merge updates from dev
GroovinChip Apr 15, 2021
59339d7
Merge updates from dev
GroovinChip Apr 15, 2021
2875d99
Use proper padding and border radius for small and large PushButtons
GroovinChip Apr 16, 2021
6674a0b
Test PushButton in example app
GroovinChip Apr 16, 2021
2d9d2f0
Merge updates from dev and update PushButton, Typography
GroovinChip Apr 17, 2021
14614d7
Export push_button_theme.dart, move todo
GroovinChip Apr 17, 2021
783917f
Documentation updates
GroovinChip Apr 17, 2021
63c97c5
Revert stubs
GroovinChip Apr 17, 2021
e040b0e
Update version and CHANGELOG.md
GroovinChip Apr 17, 2021
585b963
Ensure text color is correct when PushButton is disabled
GroovinChip Apr 17, 2021
66fffbd
Add PushButton section to README.md
GroovinChip Apr 17, 2021
ae3a582
Try embedding PushButton images as an album
GroovinChip Apr 17, 2021
d4cc436
Try embedding PushButton images as an album (2)
GroovinChip Apr 17, 2021
5eaf85a
Revert to individual images
GroovinChip Apr 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.0.5]
* Adds the `PushButton` widget along with `PushButtonTheme` and `PushButtonThemeData`
* Removes the `height` property from `Typography`'s `TextStyle`s
* Updates `Typography.headline`'s weight and letter spacing

## [0.0.4]
* Major theme refactor that more closely resembles flutter/material and flutter/cupertino
* The `Style` class is now `MacosThemeData`
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Implements Apple's macOS Design System in Flutter. Based on the official documen
- [Layout](#layout)
- [Scaffold](#scaffold)
- [Buttons](#buttons)
- [PushButton](#pushbutton)
- [Switch](#switch)
- [Indicators](#indicators)
- [ProgressCircle](#progresscircle)
Expand Down Expand Up @@ -37,13 +38,27 @@ dragging left or right. See the documentation for all customization options.
<img src="https://imgur.com/jTPXGuq.gif" width="75%"/>

# Buttons

## PushButton

<img src="https://imgur.com/D7CSk09.jpg"/>
<img src="https://imgur.com/JDPdmWo.jpg"/>
<img src="https://imgur.com/E5gECIP.jpg"/>
<img src="https://imgur.com/lpT18wZ.jpg"/>
<img src="https://imgur.com/W8ZA9nv.jpg"/>
<img src="https://imgur.com/uKRyLph.jpg"/>
<img src="https://imgur.com/w4hvYZQ.jpg"/>
<img src="https://imgur.com/ztwz6ut.jpg"/>

## Switch
<img src="https://imgur.com/IBh5jkz.jpg" width="50%" height="50%"/>

<img src="https://imgur.com/qK1VCVr.jpg" width="50%" height="50%"/>

# Indicators

## ProgressCircle

A `ProgressCircle` can be either determinate or indeterminate. If indeterminate, Flutter's
`CupertinoActivityIndicator` will be shown.

Expand Down
10 changes: 6 additions & 4 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:macos_ui/macos_ui.dart';
import 'package:provider/provider.dart';

import 'theme.dart';

void main() {
Expand All @@ -18,7 +19,7 @@ class MyApp extends StatelessWidget {
theme: MacosThemeData.light(),
darkTheme: MacosThemeData.dark(),
themeMode: ThemeMode.dark,
debugShowCheckedModeBanner: false, //yay!
debugShowCheckedModeBanner: false,
home: Demo(),
);
},
Expand All @@ -43,9 +44,10 @@ class _DemoState extends State<Demo> {
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Switch(
value: value,
onChanged: (v) => setState(() => value = v),
PushButton(
buttonSize: ButtonSize.small,
child: Text('Button'),
onPressed: () {},
),
],
),
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.4"
version: "0.0.5"
matcher:
dependency: transitive
description:
Expand Down
16 changes: 13 additions & 3 deletions lib/macos_ui.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
library macos_ui;

/// todo: package-level docs
export 'package:flutter/cupertino.dart'
show CupertinoColors, CupertinoDynamicColor;
export 'package:flutter/material.dart'
Expand All @@ -11,15 +12,24 @@ export 'package:flutter/material.dart'
PageTransitionsBuilder,
FlutterLogo,
CircleAvatar;
export 'package:flutter/widgets.dart' hide Icon, TextBox;

/// todo: package-level docs
export 'package:flutter/widgets.dart' hide Icon, IconTheme, TextBox;

export 'src/buttons/push_button.dart';
export 'src/buttons/push_button_theme.dart';
export 'src/buttons/switch.dart';
export 'src/buttons/switch.dart';
export 'src/buttons/switch.dart';
export 'src/buttons/switch.dart';
export 'src/indicators/progress_indicators.dart';
export 'src/indicators/progress_indicators.dart';
export 'src/layout/scaffold.dart';
export 'src/layout/scaffold.dart';
export 'src/layout/scaffold.dart';
export 'src/macos_app.dart';
export 'src/macos_app.dart';
export 'src/styles/macos_theme.dart';
export 'src/styles/macos_theme_data.dart';
export 'src/styles/typography.dart';
export 'src/styles/typography.dart';
export 'src/util.dart';
export 'src/util.dart';
251 changes: 251 additions & 0 deletions lib/src/buttons/push_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import 'package:flutter/foundation.dart';
import 'package:macos_ui/macos_ui.dart';

enum ButtonSize {
large,
small,
}

const EdgeInsetsGeometry _kSmallButtonPadding =
EdgeInsets.symmetric(vertical: 3.0, horizontal: 8.0);
const EdgeInsetsGeometry _kLargeButtonPadding =
EdgeInsets.symmetric(vertical: 6.0, horizontal: 8.0);

const BorderRadius _kSmallButtonRadius =
const BorderRadius.all(Radius.circular(5.0));
const BorderRadius _kLargeButtonRadius =
const BorderRadius.all(Radius.circular(7.0));

/// A macOS-style button.
class PushButton extends StatefulWidget {
const PushButton({
Key? key,
required this.child,
required this.buttonSize,
this.padding,
this.color,
this.disabledColor,
this.onPressed,
this.pressedOpacity = 0.4,
this.borderRadius = const BorderRadius.all(Radius.circular(4.0)),
this.alignment = Alignment.center,
}) : assert(pressedOpacity == null ||
(pressedOpacity >= 0.0 && pressedOpacity <= 1.0)),
super(key: key);

/// The widget below this widget in the tree.
///
/// Typically a [Text] widget.
final Widget child;

/// The size of the button.
///
/// Must be either [ButtonSize.small] or [ButtonSize.large].
///
/// Small buttons have a `padding` of [_kSmallButtonPadding] and a
/// `borderRadius` of [_kSmallButtonRadius]. Large buttons have a `padding`
/// of [_kLargeButtonPadding] and a `borderRadius` of [_kLargeButtonRadius].
final ButtonSize buttonSize;

/// The amount of space to surround the child inside the bounds of the button.
///
/// Leave blank to use the default padding provided by [_kSmallButtonPadding]
/// or [_kLargeButtonPadding].
final EdgeInsetsGeometry? padding;

/// The color of the button's background.
final Color? color;

/// The color of the button's background when the button is disabled.
///
/// Ignored if the [PushButton] doesn't also have a [color].
///
/// Defaults to [CupertinoColors.quaternarySystemFill] when [color] is
/// specified. Must not be null.
final Color? disabledColor;

/// The callback that is called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback? onPressed;

/// The opacity that the button will fade to when it is pressed.
/// The button will have an opacity of 1.0 when it is not pressed.
///
/// This defaults to 0.4. If null, opacity will not change on pressed if using
/// your own custom effects is desired.
final double? pressedOpacity;

/// The radius of the button's corners when it has a background color.
///
/// Leave blank to use the default radius provided by [_kSmallButtonRadius]
/// or [_kLargeButtonRadius].
final BorderRadius? borderRadius;

/// The alignment of the button's [child].
///
/// Typically buttons are sized to be just big enough to contain the child and its
/// [padding]. If the button's size is constrained to a fixed size, for example by
/// enclosing it with a [SizedBox], this property defines how the child is aligned
/// within the available space.
///
/// Always defaults to [Alignment.center].
final AlignmentGeometry alignment;

/// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// enable a button, set its [onPressed] property to a non-null value.
bool get enabled => onPressed != null;

@override
_PushButtonState createState() => _PushButtonState();
}

class _PushButtonState extends State<PushButton>
with SingleTickerProviderStateMixin {
// Eyeballed values. Feel free to tweak.
static const Duration kFadeOutDuration = Duration(milliseconds: 10);
static const Duration kFadeInDuration = Duration(milliseconds: 100);
final Tween<double> _opacityTween = Tween<double>(begin: 1.0);

late AnimationController _animationController;
late Animation<double> _opacityAnimation;

@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 200),
value: 0.0,
vsync: this,
);
_opacityAnimation = _animationController
.drive(CurveTween(curve: Curves.decelerate))
.drive(_opacityTween);
_setTween();
}

@override
void didUpdateWidget(PushButton old) {
super.didUpdateWidget(old);
_setTween();
}

void _setTween() {
_opacityTween.end = widget.pressedOpacity ?? 1.0;
}

@override
void dispose() {
_animationController.dispose();
super.dispose();
}

bool _buttonHeldDown = false;

void _handleTapDown(TapDownDetails event) {
if (!_buttonHeldDown) {
_buttonHeldDown = true;
_animate();
}
}

void _handleTapUp(TapUpDetails event) {
if (_buttonHeldDown) {
_buttonHeldDown = false;
_animate();
}
}

void _handleTapCancel() {
if (_buttonHeldDown) {
_buttonHeldDown = false;
_animate();
}
}

void _animate() {
if (_animationController.isAnimating) return;
final bool wasHeldDown = _buttonHeldDown;
final TickerFuture ticker = _buttonHeldDown
? _animationController.animateTo(1.0, duration: kFadeOutDuration)
: _animationController.animateTo(0.0, duration: kFadeInDuration);
ticker.then<void>((void value) {
if (mounted && wasHeldDown != _buttonHeldDown) _animate();
});
}

@override
Widget build(BuildContext context) {
final bool enabled = widget.enabled;
final MacosThemeData theme = MacosTheme.of(context);
final Color? backgroundColor = widget.color == null
? theme.pushButtonTheme.color
: CupertinoDynamicColor.maybeResolve(widget.color, context);

final Color? disabledColor = widget.disabledColor == null
? theme.pushButtonTheme.disabledColor
: CupertinoDynamicColor.maybeResolve(widget.disabledColor, context);

final EdgeInsetsGeometry? buttonPadding = widget.padding == null
? widget.buttonSize == ButtonSize.small
? _kSmallButtonPadding
: _kLargeButtonPadding
: widget.padding;

final BorderRadius? borderRadius = widget.borderRadius == null
? widget.buttonSize == ButtonSize.small
? _kSmallButtonRadius
: _kLargeButtonRadius
: widget.borderRadius;

final Color? foregroundColor = widget.enabled
? textLuminance(backgroundColor!)
: theme.brightness!.isDark
? Color.fromRGBO(255, 255, 255, 0.25)
: Color.fromRGBO(0, 0, 0, 0.25);

final TextStyle textStyle =
theme.typography!.headline!.copyWith(color: foregroundColor);

return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: enabled ? _handleTapDown : null,
onTapUp: enabled ? _handleTapUp : null,
onTapCancel: enabled ? _handleTapCancel : null,
onTap: widget.onPressed,
child: Semantics(
button: true,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: 49,
minHeight: 20,
),
child: FadeTransition(
opacity: _opacityAnimation,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: borderRadius,
color: !enabled
? CupertinoDynamicColor.resolve(disabledColor!, context)
: backgroundColor,
),
child: Padding(
padding: buttonPadding!,
child: Align(
alignment: widget.alignment,
widthFactor: 1.0,
heightFactor: 1.0,
//todo: show proper text color in light theme
child: DefaultTextStyle(
style: textStyle,
child: widget.child,
),
),
),
),
),
),
),
);
}
}
Loading