Skip to content
Merged
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
8 changes: 5 additions & 3 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github: [adeeteya]
buy_me_a_coffee: adeeteya
patreon: adeeteya
liberapay: adeeteya
open_collective: adeeteya
ko_fi: adeeteya
open_collective: adeeteya
liberapay: adeeteya
issuehunt: adeeteya
buy_me_a_coffee: adeeteya
thanks_dev: adeeteya
2 changes: 1 addition & 1 deletion .github/workflows/build-and-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:
- name: Upload Windows Release Artifact to GitHub Release
shell: cmd
run: |
gh release upload ${{ inputs.version_number }} D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\MarkdownEditor-Windows.exe
gh release upload ${{ inputs.version_number }} "%GITHUB_WORKSPACE%\MarkdownEditor-Windows.exe"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}

Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
🖌️ updated dependencies
Added LaTex support
Added Horizontal Swipe to Switch between Preview and Editing View in Single View Mode
Added Default Folder for Opening and Saving .md files
Added Print/Save as Pdf Option
Added the option to check/uncheck checkboxes in preview mode
Added Convenient way to add tables
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ Please star⭐ the repo if you like what you see😊.
<a href="https://github.com/adeeteya/FlutterMarkdownEditor/releases/latest/download/MarkdownEditor-Linux-rpm.rpm">
<img alt="Download .rpm" src="https://img.shields.io/static/v1?label=Download&message=.rpm&color=EE0000&style=for-the-badge&logo=redhat&logoColor=white&logoSize=auto">
</a>
<br>
<br>
<a href="https://snapcraft.io/markdown-editor">
<img alt="Get it from the Snap Store" src=https://snapcraft.io/en/dark/install.svg />
</a>
</td>
</tr>

Expand Down Expand Up @@ -96,23 +101,33 @@ Please star⭐ the repo if you like what you see😊.
- [x] Horizontal Swipe to Switch between Preview and Editing View in Single View Mode
- [x] Default Folder for Opening and Saving .md files
- [x] Added Print/Save as Pdf Option
- [x] Added the option to check/uncheck checkboxes in preview mode

## 📸 Screenshots

<img alt="Light Mode Image" src="screenshots/screenshot_1.png" height="587px" width="256px"/> <img alt="Dark Mode Image" src="screenshots/screenshot_2.png" height="587px" width="256px"/> <img alt="File Explorer Image" src="screenshots/screenshot_3.png" height="587px" width="256px"/> <img alt="Add Link Image" src="screenshots/screenshot_4.png" height="587px" width="256px"/> <img alt="Markdown Preview Image" src="screenshots/screenshot_5.png" height="587px" width="256px"/> <img alt="Markdown Editor Image" src="screenshots/screenshot_6.png" height="587px" width="256px"/>

## 🔌 Plugins

| Name | Usage |
|-----------------------------------------------------------------------------|-------------------------------------------------------------|
| [**flutter_markdown**](https://pub.dev/packages/flutter_markdown) | To render markdown text |
| [**permission_handler**](https://pub.dev/packages/permission_handler) | To get storage permissions for opening and saving .md files |
| [**url_launcher**](https://pub.dev/packages/url_launcher) | To launch markdown links |
| [**file_picker**](https://pub.dev/packages/file_picker) | To open markdown files directly from the app |
| [**expandable**](https://pub.dev/packages/expandable) | To create expandable header buttons |
| [**flutter_localizations**](https://pub.dev/packages/flutter_localizations) | Internationalizing app |
| [**intl**](https://pub.dev/packages/intl) | Provides internationalization and localization facilities |
| [**flutter_lints**](https://pub.dev/packages/flutter_lints) | For linting |
| Name | Usage |
|---------------------------------------------------------------------------------------------|-------------------------------------------------------------|
| [**expandable**](https://pub.dev/packages/expandable) | To create expandable header buttons |
| [**file_picker**](https://pub.dev/packages/file_picker) | To open markdown files directly from the app |
| [**flutter_localizations**](https://pub.dev/packages/flutter_localizations) | Internationalizing app |
| [**flutter_math_fork**](https://pub.dev/packages/flutter_math_fork) | Help with LaTeX support |
| [**flutter_svg**](https://pub.dev/packages/flutter_svg) | To Display Svg Images |
| [**flutter_widget_from_html_core**](https://pub.dev/packages/flutter_widget_from_html_core) | To add html display support |
| [**html**](https://pub.dev/packages/html) | To help display html format |
| [**htmltopdfwidgets**](https://pub.dev/packages/htmltopdfwidgets) | To convert html to pdf for printing |
| [**intl**](https://pub.dev/packages/intl) | Provides internationalization and localization facilities |
| [**markdown**](https://pub.dev/packages/markdown) | To convert markdown to html format |
| [**markdown_widget**](https://pub.dev/packages/markdown_widget) | To help display markdown/custom markdown |
| [**pdf**](https://pub.dev/packages/pdf) | To help save/create a pdf |
| [**permission_handler**](https://pub.dev/packages/permission_handler) | To get storage permissions for opening and saving .md files |
| [**printing**](https://pub.dev/packages/printing) | To help printing a pdf |
| [**shared_preferences**](https://pub.dev/packages/shared_preferences) | To store device preferences for persistence |
| [**url_launcher**](https://pub.dev/packages/url_launcher) | To launch markdown links |
| [**flutter_lints**](https://pub.dev/packages/flutter_lints) | For linting |

## 🤓 Author

Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

| Version | Supported |
|---------|--------------------|
| 2.0.0 | :white_check_mark: |
| 1.4.1 | :white_check_mark: |
| 1.4.0 | :white_check_mark: |
| 1.3.0 | :white_check_mark: |
Expand Down
87 changes: 86 additions & 1 deletion lib/home.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;

import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
Expand All @@ -10,6 +11,7 @@ import 'package:htmltopdfwidgets/htmltopdfwidgets.dart' as html2pdf;
import 'package:markdown/markdown.dart' as md;
import 'package:markdown_editor/device_preference_notifier.dart';
import 'package:markdown_editor/l10n/generated/app_localizations.dart';
import 'package:markdown_editor/widgets/MarkdownBody/custom_checkbox.dart';
import 'package:markdown_editor/widgets/MarkdownBody/custom_image_config.dart';
import 'package:markdown_editor/widgets/MarkdownBody/custom_text_node.dart';
import 'package:markdown_editor/widgets/MarkdownBody/latex_node.dart';
Expand All @@ -33,6 +35,9 @@ class _HomeState extends State<Home> {
static const _methodChannel = MethodChannel(
"com.adeeteya.markdown_editor/channel",
);
static final RegExp _taskListLinePattern = RegExp(
r'^(?:[-+*]|\d+[.)])\s+\[(?: |x|X)\]',
);
String _filePath = "/storage/emulated/0/Download";
String _fileName = 'Markdown';
bool _isPreview = false;
Expand Down Expand Up @@ -263,6 +268,7 @@ class _HomeState extends State<Home> {
final config = isDark
? MarkdownConfig.darkConfig
: MarkdownConfig.defaultConfig;
int checkboxIndex = -1;
return Card(
child: SizedBox(
height: double.infinity,
Expand All @@ -283,7 +289,22 @@ class _HomeState extends State<Home> {
CustomTextNode(node.textContent, config, visitor),
richTextBuilder: Text.rich,
),
config: config.copy(configs: [CustomImgConfig()]),
config: config.copy(
configs: [
CustomImgConfig(),
CheckBoxConfig(
builder: (checked) {
checkboxIndex++;
final currentIndex = checkboxIndex;
return CustomCheckbox(
key: ValueKey('markdown-task-checkbox-$currentIndex'),
checked: checked,
onChanged: () => _toggleTaskListCheckbox(currentIndex),
);
},
),
],
),
),
),
),
Expand Down Expand Up @@ -346,6 +367,70 @@ class _HomeState extends State<Home> {
);
}

List<int> _taskListMarkerPositions(String text) {
final positions = <int>[];
final lines = text.split('\n');
var offset = 0;

for (final line in lines) {
var sanitizedLine = line;
if (sanitizedLine.endsWith('\r')) {
sanitizedLine = sanitizedLine.substring(0, sanitizedLine.length - 1);
}
sanitizedLine = sanitizedLine.trimLeft();
while (sanitizedLine.startsWith('>')) {
sanitizedLine = sanitizedLine.substring(1).trimLeft();
}
if (_taskListLinePattern.hasMatch(sanitizedLine)) {
final bracketIndex = line.indexOf('[');
if (bracketIndex != -1) {
positions.add(offset + bracketIndex);
}
}
offset += line.length + 1;
}

return positions;
}

void _toggleTaskListCheckbox(int index) {
final markerPositions = _taskListMarkerPositions(_inputText);
if (index < 0 || index >= markerPositions.length) {
return;
}
final markerStart = markerPositions[index];
if (markerStart + 2 >= _inputText.length) {
return;
}
final currentState = _inputText[markerStart + 1];
final newState = (currentState == 'x' || currentState == 'X') ? ' ' : 'x';
final updatedText = _inputText.replaceRange(
markerStart,
markerStart + 3,
'[$newState]',
);
final currentSelection = _textEditingController.selection;
final collapsedSelection = currentSelection.isValid
? TextSelection(
baseOffset: math.min(
currentSelection.baseOffset,
updatedText.length,
),
extentOffset: math.min(
currentSelection.extentOffset,
updatedText.length,
),
)
: TextSelection.collapsed(offset: updatedText.length);
setState(() {
_inputText = updatedText;
_textEditingController.value = TextEditingValue(
text: updatedText,
selection: collapsedSelection,
);
});
}

@override
Widget build(BuildContext context) {
return GestureDetector(
Expand Down
25 changes: 25 additions & 0 deletions lib/widgets/MarkdownBody/custom_checkbox.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';

class CustomCheckbox extends StatelessWidget {
final bool checked;
final VoidCallback onChanged;

const CustomCheckbox({
super.key,
required this.checked,
required this.onChanged,
});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Checkbox(
value: checked,
onChanged: (_) => onChanged(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
);
}
}
8 changes: 6 additions & 2 deletions lib/widgets/MarkdownBody/custom_image_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ class CustomImgConfig extends ImgConfig {
try {
final w = attrs['width'];
final h = attrs['height'];
if (w != null && w.trim().isNotEmpty) width = double.parse(w);
if (h != null && h.trim().isNotEmpty) height = double.parse(h);
if (w != null && w.trim().isNotEmpty) {
width = double.tryParse(w) ?? 0;
}
if (h != null && h.trim().isNotEmpty) {
height = double.tryParse(h) ?? 0;
}
} catch (_) {}

final alt = attrs['alt'] ?? '';
Expand Down
2 changes: 1 addition & 1 deletion linux/markdown_editor.desktop
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Desktop Entry]
Version=1.4.1
Version=2.0.0
Name=Markdown Editor
Comment=Markdown Editor is a lightweight and intuitive app for creating and editing .md files with ease
Exec=/usr/bin/markdown-editor
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: A Flutter app to edit and save markdown files

publish_to: 'none'

version: 1.4.1+7
version: 2.0.0+8

environment:
sdk: ^3.9.2
Expand Down
7 changes: 4 additions & 3 deletions scripts/windows-setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define MyAppExeName "MarkdownEditor.exe"
#define MyAppContact "adeeteya@gmail.com"
#define MyAppCopyright "Copyright (C) 2025 Adeeteya"
#define Workspace GetEnv("GITHUB_WORKSPACE")

[Setup]
AppId={{B23EEE54-4907-4B4A-9739-A56313D52E7A}
Expand All @@ -21,9 +22,9 @@ AppContact={#MyAppContact}
AppCopyright={#MyAppCopyright}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
OutputDir=D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\
OutputDir={#Workspace}
OutputBaseFilename=MarkdownEditor-Windows
SetupIconFile=D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\windows\runner\resources\app_icon.ico
SetupIconFile={#Workspace}\windows\runner\resources\app_icon.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern
Expand All @@ -43,7 +44,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#Workspace}\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Expand Down
2 changes: 1 addition & 1 deletion snap/gui/markdown-editor.desktop
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Desktop Entry]
Version=1.4.1
Version=2.0.0
Name=Markdown Editor
Comment=Markdown Editor is an app for creating and editing .md files with ease
Exec=markdown-editor
Expand Down
2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: markdown-editor
version: 1.4.1
version: 2.0.0
summary: Markdown Editor is an app for creating and editing .md files with ease.
description: Markdown Editor is a lightweight and intuitive app for creating and editing .md files with ease.
icon: snap/gui/markdown-editor.png
Expand Down