live_markdown_editor 0.2.0
live_markdown_editor: ^0.2.0 copied to clipboard
A source-preserving Flutter Markdown editor with raw, live, and read modes.
example/lib/main.dart
import 'package:flutter/widgets.dart';
import 'package:live_markdown_editor/live_markdown_editor.dart';
import 'cupertino_tab.dart';
import 'demo_settings.dart';
import 'foundation_tab.dart';
import 'material_tab.dart';
void main() => runApp(const EditorDemoApp());
/// Three-tab demo host: a framework-neutral shell (widgets layer only) that
/// owns the shared controller, shared settings, and status line, and embeds
/// the editor in a Foundation (neutral), Material, and Cupertino tab.
final class EditorDemoApp extends StatefulWidget {
const EditorDemoApp({super.key});
@override
State<EditorDemoApp> createState() => _EditorDemoAppState();
}
final class _EditorDemoAppState extends State<EditorDemoApp> {
late final MarkdownEditorController _controller = MarkdownEditorController(
text: sampleMarkdown,
);
DemoSettings _settings = const DemoSettings();
var _tabIndex = 0;
String _status = 'Edits stay in memory in this demo.';
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _updateStatus(String message) => setState(() => _status = message);
void _setMode(MarkdownEditorMode mode) =>
setState(() => _controller.mode = mode);
@override
Widget build(BuildContext context) {
final hostTheme = _settings.dark
? MarkdownEditorThemeData.dark()
: MarkdownEditorThemeData.light();
return WidgetsApp(
color: hostTheme.backgroundColor,
debugShowCheckedModeBanner: false,
builder: (context, child) => ColoredBox(
color: hostTheme.backgroundColor,
child: SafeArea(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: demoContentMaxWidth),
child: Column(
children: [
_TabBar(
index: _tabIndex,
theme: hostTheme,
onSelect: _selectTab,
),
Expanded(
child: switch (_tabIndex) {
0 => FoundationTab(
controller: _controller,
settings: _settings,
onSettingsChanged: (settings) =>
setState(() => _settings = settings),
onModeChanged: _setMode,
status: _status,
onStatus: _updateStatus,
),
1 => MaterialConfigurationTab(
controller: _controller,
settings: _settings,
onSettingsChanged: (settings) =>
setState(() => _settings = settings),
onModeChanged: _setMode,
status: _status,
onStatus: _updateStatus,
),
_ => CupertinoConfigurationTab(
controller: _controller,
settings: _settings,
onSettingsChanged: (settings) =>
setState(() => _settings = settings),
onModeChanged: _setMode,
status: _status,
onStatus: _updateStatus,
),
},
),
],
),
),
),
),
),
);
}
void _selectTab(int index) => setState(() => _tabIndex = index);
}
final class _TabBar extends StatelessWidget {
const _TabBar({
required this.index,
required this.theme,
required this.onSelect,
});
final int index;
final MarkdownEditorThemeData theme;
final ValueChanged<int> onSelect;
static const labels = ['foundation', 'material', 'cupertino'];
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: theme.codeBackgroundColor),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var i = 0; i < labels.length; i++)
Semantics(
button: true,
selected: i == index,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onSelect(i),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 10,
),
child: Text(
labels[i],
style: theme.bodyStyle.copyWith(
color: i == index ? theme.accentColor : theme.textColor,
fontWeight: i == index
? FontWeight.w700
: FontWeight.w400,
),
),
),
),
),
],
),
),
);
}
}