diff --git a/assets/images/icons/printer-network.png b/assets/images/icons/printer-network.png
new file mode 100644
index 00000000..207ffa29
Binary files /dev/null and b/assets/images/icons/printer-network.png differ
diff --git a/assets/images/icons/printer-network.svg b/assets/images/icons/printer-network.svg
new file mode 100644
index 00000000..fbc4c792
--- /dev/null
+++ b/assets/images/icons/printer-network.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/icons/printer.png b/assets/images/icons/printer.png
new file mode 100644
index 00000000..251a4c7e
Binary files /dev/null and b/assets/images/icons/printer.png differ
diff --git a/assets/images/icons/printer.svg b/assets/images/icons/printer.svg
new file mode 100644
index 00000000..5ce86f0e
--- /dev/null
+++ b/assets/images/icons/printer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lib/view/pages/page_items.dart b/lib/view/pages/page_items.dart
index a756db8b..b065f254 100644
--- a/lib/view/pages/page_items.dart
+++ b/lib/view/pages/page_items.dart
@@ -18,6 +18,7 @@ import 'package:settings/view/pages/multitasking/multi_tasking_page.dart';
import 'package:settings/view/pages/notifications/notifications_page.dart';
import 'package:settings/view/pages/online_accounts/online_accounts_page.dart';
import 'package:settings/view/pages/power/power_page.dart';
+import 'package:settings/view/pages/printers/printers_page.dart';
import 'package:settings/view/pages/privacy/privacy_page.dart';
import 'package:settings/view/pages/region_and_language/region_and_language_page.dart';
import 'package:settings/view/pages/removable_media/removable_media_page.dart';
@@ -139,10 +140,11 @@ List getPageItems(BuildContext context) => [
hasAppBar: false,
),
SettingsPageItem(
- titleBuilder: (context) => const Text('Printers'),
+ titleBuilder: PrintersPage.createTitle,
iconBuilder: (context, selected) => const Icon(YaruIcons.printer),
- builder: (_) => const Center(child: Text('Printers')),
+ builder: PrintersPage.create,
title: context.l10n.printersPageTitle,
+ searchMatches: PrintersPage.searchMatches,
),
SettingsPageItem(
titleBuilder: RemovableMediaPage.createTitle,
diff --git a/lib/view/pages/printers/printers_model.dart b/lib/view/pages/printers/printers_model.dart
new file mode 100644
index 00000000..3ff7516a
--- /dev/null
+++ b/lib/view/pages/printers/printers_model.dart
@@ -0,0 +1,23 @@
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:safe_change_notifier/safe_change_notifier.dart';
+
+class PrintersModel extends SafeChangeNotifier {
+ void openPrinterSettings() {
+ Process.run('system-config-printer', ['&']);
+ }
+
+ Future> loadPrinters() async {
+ final printerNames = [];
+ await Process.run('lpinfo', ['-v']).then((value) {
+ printerNames.addAll(const LineSplitter().convert(value.stdout));
+ printerNames.retainWhere(
+ (element) =>
+ element.contains('network dnssd://') ||
+ element.contains('network ipps://'),
+ );
+ });
+ return printerNames;
+ }
+}
diff --git a/lib/view/pages/printers/printers_page.dart b/lib/view/pages/printers/printers_page.dart
new file mode 100644
index 00000000..f11ffade
--- /dev/null
+++ b/lib/view/pages/printers/printers_page.dart
@@ -0,0 +1,282 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:settings/constants.dart';
+import 'package:settings/l10n/l10n.dart';
+import 'package:settings/view/pages/printers/printers_model.dart';
+import 'package:settings/view/pages/settings_page.dart';
+import 'package:yaru_icons/yaru_icons.dart';
+import 'package:yaru_widgets/yaru_widgets.dart';
+
+class PrintersPage extends StatefulWidget {
+ const PrintersPage({super.key});
+
+ static Widget create(BuildContext context) {
+ // final service = Provider.of(context, listen: false);
+ return ChangeNotifierProvider(
+ create: (_) => PrintersModel(),
+ child: const PrintersPage(),
+ );
+ }
+
+ static Widget createTitle(BuildContext context) =>
+ Text(context.l10n.printersPageTitle);
+
+ static bool searchMatches(String value, BuildContext context) =>
+ value.isNotEmpty
+ ? context.l10n.printersPageTitle
+ .toLowerCase()
+ .contains(value.toLowerCase())
+ : false;
+
+ @override
+ State createState() => _PrintersPageState();
+}
+
+class _PrintersPageState extends State {
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final model = context.watch();
+ return SettingsPage(
+ children: [
+ ChangeNotifierProvider.value(
+ value: model,
+ child: const _Header(),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ SizedBox(
+ width: kDefaultWidth,
+ child: FutureBuilder>(
+ future: model.loadPrinters(),
+ builder: (context, snapshot) {
+ return snapshot.hasData
+ ? ListView.separated(
+ itemCount: snapshot.data!.length,
+ itemBuilder: (context, index) {
+ return _PrinterSection(name: snapshot.data![index]);
+ },
+ shrinkWrap: true,
+ separatorBuilder: (context, index) {
+ return const SizedBox(
+ height: kYaruPagePadding,
+ );
+ },
+ )
+ : const YaruLinearProgressIndicator();
+ },
+ ),
+ )
+ ],
+ );
+ }
+}
+
+class _PrinterSection extends StatelessWidget {
+ const _PrinterSection({
+ required this.name,
+ });
+
+ final String name;
+
+ @override
+ Widget build(BuildContext context) {
+ return YaruSection(
+ headline: Text(name),
+ child: YaruTile(
+ trailing: Row(
+ children: [
+ SizedBox(
+ width: 70,
+ child: Image.asset(
+ 'assets/images/icons/printer.png',
+ fit: BoxFit.fill,
+ ),
+ ),
+ const SizedBox(
+ width: 20,
+ ),
+ const SizedBox(
+ height: 100,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(
+ child: Text(
+ 'Model XYZ',
+ overflow: TextOverflow.ellipsis,
+ ),
+ ), // printer.type
+ SizedBox(
+ height: 3,
+ ),
+ Flexible(
+ child: Text('bla', overflow: TextOverflow.ellipsis),
+ ), // printer.location
+ // printer.status
+ ],
+ ),
+ )
+ ],
+ ),
+ leading: Row(
+ children: [
+ const SizedBox(
+ height: 40,
+ child: OutlinedButton(
+ onPressed: null, // printer.activejobs ?
+ child: Text(
+ 'No active jobs',
+ // style: TextStyle(
+ // color: Theme.of(context).disabledColor),
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ YaruOptionButton(
+ child: const Icon(YaruIcons.settings),
+ onPressed: () => {},
+ ),
+ ],
+ ),
+ enabled: true,
+ ),
+ );
+ }
+}
+
+class _Header extends StatelessWidget {
+ const _Header();
+
+ @override
+ Widget build(BuildContext context) {
+ final model = context.watch();
+ return SizedBox(
+ width: kDefaultWidth,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (_) => StatefulBuilder(
+ builder: (context, setState) {
+ return AddPrinterDialog(
+ onConfirm: () {
+ Navigator.of(context).pop();
+ return ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Icon(
+ YaruIcons.printer,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ ),
+ );
+ },
+ title: 'Add Printer',
+ children: [
+ Icon(
+ YaruIcons.printer,
+ color: Theme.of(context).colorScheme.primary,
+ size: 50,
+ )
+ ],
+ );
+ },
+ ),
+ );
+ },
+ child: const Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Icon(YaruIcons.plus),
+ Padding(
+ padding: EdgeInsets.only(left: 12.0, right: 2),
+ child: Text('Add a printer'),
+ )
+ ],
+ ),
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ YaruOptionButton(
+ onPressed: model.openPrinterSettings,
+ child: const Icon(YaruIcons.settings),
+ )
+ ],
+ ),
+ );
+ }
+}
+
+class AddPrinterDialog extends StatelessWidget {
+ const AddPrinterDialog({
+ super.key,
+ this.onConfirm,
+ this.title,
+ this.children,
+ });
+
+ final Function()? onConfirm;
+ final String? title;
+ final List? children;
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 500,
+ child: SimpleDialog(
+ titlePadding: EdgeInsets.zero,
+ title: YaruDialogTitleBar(
+ title: Text(title ?? ''),
+ ),
+ contentPadding: EdgeInsets.zero,
+ children: [
+ const Icon(
+ YaruIcons.printer,
+ size: 80,
+ ),
+ const SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: SizedBox(
+ width: kDefaultWidth / 3,
+ child: Row(
+ children: [
+ Expanded(
+ child: OutlinedButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: Text(context.l10n.cancel),
+ ),
+ ),
+ const SizedBox(
+ width: 8,
+ ),
+ Expanded(
+ child: ElevatedButton(
+ onPressed: onConfirm,
+ child: Text(
+ context.l10n.confirm,
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index ac512f3b..6f679057 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -80,3 +80,4 @@ flutter:
- assets/images/appearance/auto-hide-dock-mode/
- assets/rive/
- assets/pdf_assets/
+ - assets/images/icons/
\ No newline at end of file