diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d3a6d06..d09c4145 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
## [0.0.3]
+* Implemented `Checkbox`
* Implemented `ProgressCircle` and `ProgressBar`
* Implemented the `Switch` widget
diff --git a/README.md b/README.md
index e3ae3278..23237015 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,12 @@ dragging left or right. See the documentation for all customization options.
# Buttons
+## Checkbox
+
+| off | on | mixed |
+| --- | -- | ----- |
+|  |  |  |
+
## PushButton
diff --git a/example/.gitignore b/example/.gitignore
index 0fa6b675..527ef53e 100644
--- a/example/.gitignore
+++ b/example/.gitignore
@@ -44,3 +44,5 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
+
+/windows/
\ No newline at end of file
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 6bd962f6..e0f30067 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
title: 'macos_ui example',
theme: MacosThemeData.light(),
darkTheme: MacosThemeData.dark(),
- themeMode: ThemeMode.dark,
+ // themeMode: ThemeMode.dark,
debugShowCheckedModeBanner: false,
home: Demo(),
);
@@ -42,13 +42,17 @@ class _DemoState extends State {
child: Text('Sidebar'),
),
body: Column(
- mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
PushButton(
buttonSize: ButtonSize.small,
child: Text('Button'),
onPressed: () {},
),
+ Checkbox(
+ value: value,
+ onChanged: (v) => setState(() => value = v),
+ ),
],
),
);
diff --git a/example/pubspec.lock b/example/pubspec.lock
index ace9095e..077fec03 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -127,7 +127,7 @@ packages:
name: split_view
url: "https://pub.dartlang.org"
source: hosted
- version: "2.0.1+1"
+ version: "2.0.1+2"
stack_trace:
dependency: transitive
description:
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 5c3c4074..2f6b6b17 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -18,5 +18,3 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
-
-flutter:
diff --git a/lib/macos_ui.dart b/lib/macos_ui.dart
index 32d9a821..aaf9bf8e 100644
--- a/lib/macos_ui.dart
+++ b/lib/macos_ui.dart
@@ -1,8 +1,9 @@
library macos_ui;
/// todo: package-level docs
+export 'package:flutter/widgets.dart';
export 'package:flutter/cupertino.dart'
- show CupertinoColors, CupertinoDynamicColor;
+ show CupertinoColors, CupertinoDynamicColor, CupertinoIcons;
export 'package:flutter/material.dart'
show
Brightness,
@@ -30,6 +31,7 @@ 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/buttons/checkbox.dart';
export 'src/styles/typography.dart';
export 'src/util.dart';
export 'src/util.dart';
diff --git a/lib/src/buttons/checkbox.dart b/lib/src/buttons/checkbox.dart
new file mode 100644
index 00000000..661ba54f
--- /dev/null
+++ b/lib/src/buttons/checkbox.dart
@@ -0,0 +1,106 @@
+import 'package:macos_ui/macos_ui.dart';
+
+/// A checkbox is a type of button that lets the user choose between
+/// two opposite states, actions, or values. A selected checkbox is
+/// considered on when it contains a checkmark and off when it's empty.
+/// A checkbox is almost always followed by a title unless it appears in
+/// a checklist.
+class Checkbox extends StatelessWidget {
+ /// Creates a checkbox.
+ ///
+ /// [size] must be non-negative
+ const Checkbox({
+ Key? key,
+ required this.value,
+ required this.onChanged,
+ this.size = 16.0,
+ this.activeColor,
+ this.disabledColor = CupertinoColors.quaternaryLabel,
+ this.offBorderColor = CupertinoColors.tertiaryLabel,
+ }) : assert(size >= 0),
+ super(key: key);
+
+ /// Whether the checkbox is checked or not. If null, it'll be considered
+ /// mixed.
+ final bool? value;
+
+ /// Called whenever the state of the checkbox changes. If null, the checkbox
+ /// will be considered disabled
+ final ValueChanged? onChanged;
+
+ /// The size of the checkbox. It must be non-negative.
+ final double size;
+
+ /// The background color when the checkbox is on or mixed. If null,
+ /// [Style.activeColor] is used
+ final Color? activeColor;
+
+ /// The background color when the checkbox is disabled. [CupertinoColors.quaternaryLabel]
+ /// is used by default
+ final Color disabledColor;
+
+ /// The color of the border when the checkbox is off. [CupertinoColors.tertiaryLabel]
+ /// is used by default
+ final Color offBorderColor;
+
+ /// Whether the checkbox is mixed or not.
+ bool get isMixed => value == null;
+
+ /// Whether the checkbox is disabled or not.
+ bool get isDisabled => onChanged == null;
+
+ @override
+ Widget build(BuildContext context) {
+ assert(debugCheckHasMacosTheme(context));
+ bool isLight = context.macosTheme.brightness != Brightness.dark;
+ return GestureDetector(
+ onTap: () {
+ if (value == null || value == false) {
+ onChanged?.call(true);
+ } else {
+ onChanged?.call(false);
+ }
+ },
+ child: Container(
+ height: size,
+ width: size,
+ alignment: Alignment.center,
+ decoration: isDisabled || value == null || value == true
+ ? BoxDecoration(
+ color: CupertinoDynamicColor.resolve(
+ isDisabled
+ ? disabledColor
+ : activeColor ??
+ context.macosTheme.primaryColor ??
+ CupertinoColors.activeBlue,
+ context,
+ ),
+ borderRadius: BorderRadius.circular(4.0),
+ )
+ : BoxDecoration(
+ color: isLight ? null : CupertinoColors.tertiaryLabel,
+ border: Border.all(
+ style: isLight ? BorderStyle.solid : BorderStyle.none,
+ width: 0.5,
+ color: CupertinoDynamicColor.resolve(
+ offBorderColor,
+ context,
+ ),
+ ),
+ borderRadius: BorderRadius.circular(4.0),
+ ),
+ child: Icon(
+ isDisabled
+ ? null
+ : isMixed
+ ? CupertinoIcons.minus
+ : value == false
+ ? null
+ : CupertinoIcons.check_mark,
+ color: CupertinoColors.white,
+ size: (size - 3).clamp(0, size),
+ ),
+ ),
+ );
+ }
+}