Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ https://api.login.yahoo.com/
https://graph.facebook.com/
https://login.microsoftonline.com/
https://securetoken.google.com/
https://fonts.gstatic.com/s/notosans/v36/
https://fonts.gstatic.com/s/notosansmono/v30/
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The package is available from
[pub.dev](https://pub.dev/packages/solidui).

Coding documentation is available from [solid community
au](https://solidcommunity.au/docs/solidui)
au](https://solidcommunity.au/docs/solidui/)

## 0.3.0 Stabilise

Expand Down
63 changes: 44 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,29 +163,54 @@ SolidUI requires the following dependencies:

## Quick Start to Create an App

To create a new Solid-based app using `solidui` named `myapp` and
published by `example.com` begin with:
To quickly create a new Solid-based app, we provide a [Mason](https://pub.dev/packages/mason) template.

### Using Mason (Recommended)

1. Install Mason:

```bash
dart pub global activate mason_cli
```

2. Initialize Mason in your project (if not already):

```bash
mason init
```

3. Add the SolidUI brick:

```bash
mason add solidui --path templates/solidui/brick
```
Comment on lines +182 to +186
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README instructions use mason add solidui --path templates/solidui/brick, which will only work if the user has this repo checked out and the template directory is present. For end users starting a new project, document a mason add command that pulls the brick from a git URL (or a published brick), or explicitly state they must clone/vendor the template directory first.

Suggested change
3. Add the SolidUI brick:
```bash
mason add solidui --path templates/solidui/brick
```
3. Add the SolidUI brick (choose one of the following):
- **Recommended (no local clone required):**
```bash
mason add solidui -g https://github.com/anusii/solidui --path templates/solidui/brick
```
- **Using a local checkout of this repository:**
Make sure you have cloned this repository and are running the command from its root directory:
```bash
mason add solidui --path templates/solidui/brick
```

Copilot uses AI. Check for mistakes.

4. Create a new app from the template:

```bash
mason make solidui
```

### Template Structure

The template app consists of several files within the
`lib/` directory:

- `main.dart`: Initialises the application and launches the app.
- `app.dart`: Implements the `App()` widget, instantiating `SolidLogin()`.
- `app_scaffold.dart`: Implements `AppScaffold()` widget using `SolidScaffold()`.
- `home.dart`: Implements the `Home()` widget for the main app functionality.
- `constants/app.dart`: App-wide constants including title and description.
- `utils/is_desktop.dart`: Desktop platform detection utility.

### Synchronizing Template and Example

If you are a contributor and want to update the `example/` app from the template (or vice-versa), use the provided sync script:

```bash
flutter create --template solidui --domain com.example myapp
python3 support/sync_template_to_example.py
```

This will create a template app which we also included here under the
`example/` directory. The app consists of several files within the
`lib/` directory. `main.dart` is the main entry point to the app. Its
task in our framework is to initialise the application and then launch
the app itself. `app.dart` implements the `App()` which is typically
where we instantiate a `SolidLogin()`, often as the `child:` of a
`SolidThemeApp()`. The `SolidLogin()` provides the login page for the
app. After logging in the `AppScaffold()`, as the `child:` of the
`SolidLogin()`, is instantiated to contain the main functionality of
the app. `app_scaffold.dart` implements the `AppScaffold()` widget
which builds a `SolidScaffold()` to set up the framework for a typical
Solid app. The child is the `Home()` widget. `home.dart` implements
the `Home()` widget as the main app functionality. Constants are
defined in `constants/app.dart` and utilities such as desktop platform
detection are in `utils/is_desktop.dart`.

## SolidScaffold

The `SolidScaffold()` is the primary widget for building Solid
Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ analyzer:
exclude:
- ignore/**
- ignore/
- templates/**
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluding templates/** from analysis means CI's flutter analyze will no longer catch regressions in the Mason brick source. Consider adding a separate CI step that generates the template (e.g., via mason make) into a temp directory and runs flutter analyze on the generated app (or otherwise validates the brick), so template breakages are still detected.

Suggested change
- templates/**

Copilot uses AI. Check for mistakes.
4 changes: 2 additions & 2 deletions example/lib/app_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ class AppScaffold extends StatelessWidget {
// VERSION WIDGET.

versionConfig: SolidVersionConfig(
changelogUrl: 'https://github.com/anusii/solidui/blob/dev/'
'CHANGELOG.md',
changelogUrl:
'https://github.com/anusii/solidui/blob/dev/CHANGELOG.md',
showDate: true,
userTextStyle: TextStyle(
color: theme.colorScheme.onSurface,
Expand Down
241 changes: 210 additions & 31 deletions example/lib/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

library;

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:solidpod/solidpod.dart';
import 'package:solidui/solidui.dart';

class Home extends StatefulWidget {
const Home({super.key, required this.title});
Expand All @@ -40,44 +43,220 @@ class Home extends StatefulWidget {
}

class _HomeState extends State<Home> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _commentController = TextEditingController();
bool _isLoading = false;

@override
void initState() {
super.initState();
_loadData();
}

@override
void dispose() {
_nameController.dispose();
_commentController.dispose();
super.dispose();
}

Future<void> _loadData() async {
if (!mounted) return;
setState(() => _isLoading = true);

try {
final appDataPath = await getDataDirPath();
final filePath = PathUtils.combine(appDataPath, 'user_info.enc.ttl');

final content = await readPod(filePath, pathType: PathType.relativeToPod);

if (content != SolidFunctionCallStatus.fail.toString() &&
content != SolidFunctionCallStatus.notLoggedIn.toString() &&
content.isNotEmpty) {
final data = jsonDecode(content);
_nameController.text = data['name'] ?? '';
_commentController.text = data['comment'] ?? '';
}
} catch (e) {
debugPrint('Error loading data: $e');
} finally {
if (mounted) setState(() => _isLoading = false);
}
}

Future<void> _saveData() async {
if (!mounted) return;
setState(() => _isLoading = true);

try {
// Ensure the security key is available before writing encrypted data.
await getKeyFromUserIfRequired(
context,
const Text('Please enter your security key to save the data'),
);

if (!mounted) return;

final data = {
'name': _nameController.text,
'comment': _commentController.text,
'lastUpdated': DateTime.now().toIso8601String(),
};

final appDataPath = await getDataDirPath();
final filePath = PathUtils.combine(appDataPath, 'user_info.enc.ttl');

await writePod(
filePath,
jsonEncode(data),
encrypted: true,
pathType: PathType.relativeToPod,
);

if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Data saved to POD successfully!'),
backgroundColor: Colors.green,
),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error saving data: $e'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) setState(() => _isLoading = false);
}
}

@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Center(
child: Card(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.home,
size: 64,
color: Theme.of(context).primaryColor,
),
const SizedBox(height: 16),
Text(
widget.title,
style: Theme.of(context).textTheme.headlineMedium,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 800),
child: Column(
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.home,
size: 48,
color: Theme.of(context).primaryColor,
),
const SizedBox(width: 16),
Expanded(
child: Text(
widget.title,
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
const SizedBox(height: 24),
const Text(
'POD Data Storage Example',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(height: 8),
const Text(
'This data is stored securely and encrypted on your POD.',
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 24),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'NAME',
hintText: 'Enter your name',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
enabled: !_isLoading,
),
const SizedBox(height: 16),
TextFormField(
controller: _commentController,
decoration: const InputDecoration(
labelText: 'COMMENT',
hintText: 'Enter a comment',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.comment),
),
maxLines: 3,
enabled: !_isLoading,
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _isLoading ? null : _saveData,
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Icon(Icons.save),
label: const Text('Save to POD'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
],
),
),
const SizedBox(height: 24),
Text(
'Welcome to the SolidUI Template App!\n\n'
'This template demonstrates the key features of SolidUI:\n\n'
'• Responsive navigation (rail ↔ drawer)\n'
'• Theme switching (light/dark/system)\n'
'• Customisable About dialogues\n'
'• Version information display\n'
'• Security key management\n'
'• Status bar integration\n'
'• User information display\n\n'
'Explore the different tabs to see these features in action!',
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 24),
Card(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'About SolidUI',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
Text(
'Welcome to the SolidUI Template App!\n\n'
'This template demonstrates the key features of SolidUI:\n\n'
'• Responsive navigation (rail ↔ drawer)\n'
'• Theme switching (light/dark/system)\n'
'• Customisable About dialogues\n'
'• Version information display\n'
'• Security key management\n'
'• Status bar integration\n'
'• User information display\n\n'
'Explore the different tabs to see these features in action!',
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
],
),
),
],
),
),
),
Expand Down
Binary file added example/lib/home.dart.old
Binary file not shown.
1 change: 1 addition & 0 deletions lib/solidui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export 'src/utils/solid_file_operations.dart';
export 'src/utils/is_phone.dart';
export 'src/utils/solid_alert.dart';
export 'src/utils/solid_notifications.dart';
export 'src/utils/path_utils.dart';
export 'src/utils/solid_pod_helpers.dart'
show loginIfRequired, getKeyFromUserIfRequired;

Expand Down
2 changes: 0 additions & 2 deletions lib/src/utils/solid_file_operations_delete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ class SolidFileDeleteOperations {

final fullFilePath = PathUtils.combine(filePath, fileName);

// Delete the file (this also handles the ACL file automatically).

try {
await deleteFile(fileUrl: await getFileUrl(fullFilePath));
} catch (e) {
Expand Down
11 changes: 11 additions & 0 deletions mason.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Register bricks which can be used via mason get.
# bricks:
# sample: 0.1.0+1
# hello:
# git:
# url: https://github.com/felangel/mason
# path: bricks/hello

bricks:
solidui:
path: templates/solidui/brick
Loading