Add very basic Image support [Android only]

This adds a '+' button to the NoteEditor which allows you to add an
image from either the Gallery or Take a photo. It then accordingly adds
updates markdown.

The file is added in the same directory as the note for now.

Related to #10
This commit is contained in:
Vishesh Handa
2020-05-05 12:02:36 +02:00
parent d0ab323356
commit b17c184900
9 changed files with 167 additions and 13 deletions

View File

@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.vending.BILLING" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.

View File

@ -250,6 +250,14 @@ class Note with NotesNotifier {
return true;
}
Future<void> addImage(File file) async {
var imageFileName = p.basename(file.path);
var imagePath = p.join(parent.folderPath, imageFileName);
await file.copy(imagePath);
body = "$body\n ![Image](./$imageFileName)\n";
}
@override
int get hashCode => _filePath.hashCode;

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -159,8 +160,9 @@ class ChecklistEditorState extends State<ChecklistEditor>
Expanded(child: FocusScope(child: checklistWidget)),
],
),
bottomNavigationBar:
buildEditorBottonBar(context, widget, this, checklist.note),
bottomNavigationBar: Builder(
builder: (context) => buildEditorBottonBar(context, widget, this),
),
);
}
@ -249,6 +251,17 @@ class ChecklistEditorState extends State<ChecklistEditor>
},
);
}
@override
Future<void> addImage(File file) async {
var note = getNote();
await note.addImage(file);
setState(() {
checklist = Checklist(note);
_noteModified = true;
});
}
}
typedef TextChangedFunction = void Function(String);

View File

@ -1,7 +1,12 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/error_reporting.dart';
import 'package:share/share.dart';
import 'package:image_picker/image_picker.dart';
typedef NoteCallback = void Function(Note);
abstract class Editor {
@ -15,6 +20,7 @@ abstract class Editor {
abstract class EditorState {
Note getNote();
Future<void> addImage(File file);
}
enum DropDownChoices { Rename, DiscardChanges, Share }
@ -93,27 +99,45 @@ Widget buildEditorBottonBar(
BuildContext context,
Editor editor,
EditorState editorState,
Note note,
) {
var note = editorState.getNote();
var folderName = note.parent.pathSpec();
if (folderName.isEmpty) {
folderName = "Root Folder";
}
var s = Scaffold.of(context);
print("s $s");
return StickyBottomAppBar(
child: BottomAppBar(
elevation: 0.0,
color: Theme.of(context).scaffoldBackgroundColor,
child: Row(
children: <Widget>[
FlatButton.icon(
icon: Icon(Icons.folder),
label: Text(folderName),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
var note = editorState.getNote();
editor.moveNoteToFolderSelected(note);
showModalBottomSheet(
context: context,
builder: (c) => _buildAddBottomSheet(c, editor, editorState),
elevation: 0,
);
},
)
),
Expanded(
child: FlatButton.icon(
icon: Icon(Icons.folder),
label: Text(folderName),
onPressed: () {
var note = editorState.getNote();
editor.moveNoteToFolderSelected(note);
},
),
),
const SizedBox(
height: 32.0,
width: 32.0,
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
@ -133,3 +157,53 @@ class StickyBottomAppBar extends StatelessWidget {
);
}
}
Widget _buildAddBottomSheet(
BuildContext context,
Editor editor,
EditorState editorState,
) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.camera),
title: const Text("Take Photo"),
onTap: () async {
try {
var image = await ImagePicker.pickImage(
source: ImageSource.camera,
);
if (image != null) {
await editorState.addImage(image);
}
} catch (e) {
reportError(e, StackTrace.current);
}
Navigator.of(context).pop();
},
),
ListTile(
leading: Icon(Icons.image),
title: const Text("Add Image"),
onTap: () async {
try {
var image = await ImagePicker.pickImage(
source: ImageSource.gallery,
);
if (image != null) {
await editorState.addImage(image);
}
} catch (e) {
reportError(e, StackTrace.current);
}
Navigator.of(context).pop();
},
),
],
),
);
}

View File

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:gitjournal/core/note.dart';
@ -84,7 +85,9 @@ class JournalEditorState extends State<JournalEditor> implements EditorState {
return Scaffold(
appBar: buildEditorAppBar(widget, this, noteModified: _noteModified),
body: editor,
bottomNavigationBar: buildEditorBottonBar(context, widget, this, note),
bottomNavigationBar: Builder(
builder: (context) => buildEditorBottonBar(context, widget, this),
),
);
}
@ -101,6 +104,15 @@ class JournalEditorState extends State<JournalEditor> implements EditorState {
_noteModified = true;
});
}
@override
Future<void> addImage(File file) async {
await getNote().addImage(file);
setState(() {
_textController.text = note.body;
_noteModified = true;
});
}
}
class _NoteBodyEditor extends StatelessWidget {

View File

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:gitjournal/core/note.dart';
@ -115,7 +116,9 @@ class MarkdownEditorState extends State<MarkdownEditor> implements EditorState {
extraButtons: [extraButton],
),
body: body,
bottomNavigationBar: buildEditorBottonBar(context, widget, this, note),
bottomNavigationBar: Builder(
builder: (context) => buildEditorBottonBar(context, widget, this),
),
);
}
@ -144,6 +147,15 @@ class MarkdownEditorState extends State<MarkdownEditor> implements EditorState {
_noteModified = true;
});
}
@override
Future<void> addImage(File file) async {
await getNote().addImage(file);
setState(() {
_textController.text = note.body;
_noteModified = true;
});
}
}
class _NoteBodyEditor extends StatelessWidget {

View File

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:gitjournal/core/note.dart';
@ -81,7 +82,9 @@ class RawEditorState extends State<RawEditor> implements EditorState {
return Scaffold(
appBar: buildEditorAppBar(widget, this, noteModified: _noteModified),
body: editor,
bottomNavigationBar: buildEditorBottonBar(context, widget, this, note),
bottomNavigationBar: Builder(
builder: (context) => buildEditorBottonBar(context, widget, this),
),
);
}
@ -97,6 +100,15 @@ class RawEditorState extends State<RawEditor> implements EditorState {
_noteModified = true;
});
}
@override
Future<void> addImage(File file) async {
await getNote().addImage(file);
setState(() {
_textController.text = note.body;
_noteModified = true;
});
}
}
class _NoteEditor extends StatelessWidget {

View File

@ -258,6 +258,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
flutter_runtime_env:
dependency: "direct main"
description:
@ -373,6 +380,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
image_picker:
dependency: "direct main"
description:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.6+1"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
intl:
dependency: "direct main"
description:
@ -568,7 +589,7 @@ packages:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
pointycastle:
dependency: transitive
description:

View File

@ -51,6 +51,7 @@ dependencies:
isolate: ^2.0.3
flutter_webview_plugin:
git: https://github.com/breez/flutter_webview_plugin.git
image_picker: ^0.6.6+1
dev_dependencies:
flutter_launcher_icons: "^0.7.2"