Browse Source

资源管理器搞定

master
hejl 2 months ago
parent
commit
dc05b02663
  1. 15
      win_text_editor/.metadata
  2. 3
      win_text_editor/devtools_options.yaml
  3. 89
      win_text_editor/lib/app/app.dart
  4. 67
      win_text_editor/lib/app/menus/app_menu.dart
  5. 33
      win_text_editor/lib/app/menus/menu_actions.dart
  6. 29
      win_text_editor/lib/app/menus/menu_constants.dart
  7. 15
      win_text_editor/lib/app/models/editor_tab.dart
  8. 127
      win_text_editor/lib/app/models/file_node.dart
  9. 97
      win_text_editor/lib/app/providers/editor_provider.dart
  10. 227
      win_text_editor/lib/app/providers/file_provider.dart
  11. 60
      win_text_editor/lib/app/services/file_service.dart
  12. 31
      win_text_editor/lib/app/services/syntax_service.dart
  13. 97
      win_text_editor/lib/app/widgets/editor_pane.dart
  14. 207
      win_text_editor/lib/app/widgets/file_explorer.dart
  15. 83
      win_text_editor/lib/app/widgets/tab_bar.dart
  16. 128
      win_text_editor/lib/main.dart
  17. 223
      win_text_editor/pubspec.lock
  18. 84
      win_text_editor/pubspec.yaml
  19. 9
      win_text_editor/windows/flutter/generated_plugin_registrant.cc
  20. 3
      win_text_editor/windows/flutter/generated_plugins.cmake

15
win_text_editor/.metadata

@ -15,21 +15,6 @@ migration:
- platform: root - platform: root
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: android
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: ios
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: linux
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: macos
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: web
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
- platform: windows - platform: windows
create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f
base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f

3
win_text_editor/devtools_options.yaml

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

89
win_text_editor/lib/app/app.dart

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/menus/app_menu.dart';
import 'package:win_text_editor/app/providers/editor_provider.dart';
import 'package:win_text_editor/app/providers/file_provider.dart';
import 'package:win_text_editor/app/widgets/editor_pane.dart';
import 'package:win_text_editor/app/widgets/file_explorer.dart';
class AppScaffold extends StatelessWidget {
const AppScaffold({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FileProvider()),
ChangeNotifierProvider(create: (_) => EditorProvider()),
],
child: const Scaffold(
body: Column(
children: [
//
AppMenu(),
//
Expanded(
child: _ResizablePanel(),
),
],
),
),
);
}
}
class _ResizablePanel extends StatefulWidget {
const _ResizablePanel();
@override
State<_ResizablePanel> createState() => _ResizablePanelState();
}
class _ResizablePanelState extends State<_ResizablePanel> {
double _leftWidth = 0.2; // 20%
final double _minWidth = 100; //
final double _maxWidth = 400; //
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return LayoutBuilder(
builder: (context, constraints) {
//
final leftPanelWidth = (_leftWidth * screenWidth).clamp(_minWidth, _maxWidth);
return Row(
children: [
//
SizedBox(
width: leftPanelWidth,
child: const FileExplorer(),
),
//
GestureDetector(
behavior: HitTestBehavior.translucent,
onPanUpdate: (details) {
setState(() {
_leftWidth = ((leftPanelWidth + details.delta.dx) / screenWidth)
.clamp(_minWidth / screenWidth, _maxWidth / screenWidth);
});
},
child: MouseRegion(
cursor: SystemMouseCursors.resizeLeftRight,
child: Container(
width: 4,
color: Colors.grey[300],
),
),
),
//
Expanded(
child: const EditorPane(),
),
],
);
},
);
}
}

67
win_text_editor/lib/app/menus/app_menu.dart

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:win_text_editor/app/menus/menu_constants.dart';
import 'menu_actions.dart';
class AppMenu extends StatelessWidget {
const AppMenu({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 30,
color: Colors.grey[200],
child: Row(
children: [
_buildMenuButton(context, '文件', _buildFileMenuItems()),
_buildMenuButton(context, '编辑', _buildEditMenuItems()),
_buildMenuButton(context, '窗口', _buildWindowMenuItems()),
_buildMenuButton(context, '帮助', _buildHelpMenuItems()),
],
),
);
}
Widget _buildMenuButton(BuildContext context, String label, List<PopupMenuEntry<String>> items) {
return PopupMenuButton<String>(
offset: const Offset(0, 30),
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Text(label)),
itemBuilder: (context) => items,
onSelected: (value) => MenuActions.handleMenuAction(value, context),
);
}
List<PopupMenuEntry<String>> _buildFileMenuItems() {
return [
const PopupMenuItem<String>(value: MenuConstants.openFolder, child: Text('打开文件夹...')),
const PopupMenuItem<String>(value: MenuConstants.save, child: Text('保存')),
const PopupMenuItem<String>(value: MenuConstants.saveAs, child: Text('另存为...')),
const PopupMenuItem<String>(value: MenuConstants.exit, child: Text('退出')),
];
}
List<PopupMenuEntry<String>> _buildEditMenuItems() {
return [
const PopupMenuItem<String>(value: MenuConstants.undo, child: Text('撤销')),
const PopupMenuItem<String>(value: MenuConstants.redo, child: Text('重做')),
const PopupMenuItem<String>(value: MenuConstants.cut, child: Text('剪切')),
const PopupMenuItem<String>(value: MenuConstants.copy, child: Text('复制')),
const PopupMenuItem<String>(value: MenuConstants.paste, child: Text('粘贴')),
];
}
List<PopupMenuEntry<String>> _buildWindowMenuItems() {
return [
const PopupMenuItem<String>(value: MenuConstants.minimize, child: Text('最小化')),
const PopupMenuItem<String>(value: MenuConstants.maximize, child: Text('最大化')),
const PopupMenuItem<String>(value: MenuConstants.close, child: Text('关闭')),
];
}
List<PopupMenuEntry<String>> _buildHelpMenuItems() {
return [
const PopupMenuItem<String>(value: MenuConstants.about, child: Text('关于')),
const PopupMenuItem<String>(value: MenuConstants.help, child: Text('帮助')),
];
}
}

33
win_text_editor/lib/app/menus/menu_actions.dart

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/menus/menu_constants.dart';
import 'package:win_text_editor/app/providers/file_provider.dart';
import 'dart:io';
class MenuActions {
static Future<void> handleMenuAction(String value, BuildContext context) async {
switch (value) {
case MenuConstants.openFolder:
await _openFolder(context);
break;
case MenuConstants.exit:
_exitApplication();
break;
//
}
}
static Future<void> _openFolder(BuildContext context) async {
final fileProvider = Provider.of<FileProvider>(context, listen: false);
final String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
if (selectedDirectory != null) {
await fileProvider.loadDirectory(selectedDirectory);
}
}
static void _exitApplication() {
exit(0);
}
}

29
win_text_editor/lib/app/menus/menu_constants.dart

@ -0,0 +1,29 @@
class MenuConstants {
//
static const String fileMenu = 'file';
static const String editMenu = 'edit';
static const String windowMenu = 'window';
static const String helpMenu = 'help';
//
static const String openFolder = 'open_folder';
static const String save = 'save';
static const String saveAs = 'save_as';
static const String exit = 'exit';
//
static const String undo = 'undo';
static const String redo = 'redo';
static const String cut = 'cut';
static const String copy = 'copy';
static const String paste = 'paste';
//
static const String minimize = 'minimize';
static const String maximize = 'maximize';
static const String close = 'close';
//
static const String about = 'about';
static const String help = 'help';
}

15
win_text_editor/lib/app/models/editor_tab.dart

@ -0,0 +1,15 @@
class EditorTab {
final String id;
final String title;
final String path;
String content;
final String fileType;
EditorTab({
required this.id,
required this.title,
required this.path,
required this.content,
required this.fileType,
});
}

127
win_text_editor/lib/app/models/file_node.dart

@ -0,0 +1,127 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class FileNode {
final String name;
final String path;
final bool isDirectory;
final bool isRoot;
final int depth;
List<FileNode> children;
bool isExpanded;
FileNode({
required this.name,
required this.path,
required this.isDirectory,
this.isRoot = false,
this.depth = 0,
this.isExpanded = false,
List<FileNode>? children,
}) : children = children ?? [];
//
IconData get iconData {
if (isDirectory) {
return Icons.folder;
}
final ext = name.split('.').last.toLowerCase();
//
switch (ext) {
case 'pdf':
return Icons.picture_as_pdf;
case 'doc':
case 'docx':
return Icons.article;
case 'xls':
case 'xlsx':
return Icons.table_chart;
case 'ppt':
case 'pptx':
return Icons.slideshow;
case 'txt':
return Icons.text_snippet;
case 'dart':
return Icons.code;
case 'js':
return Icons.javascript;
case 'java':
return Icons.coffee;
case 'py':
return Icons.data_object;
case 'html':
return Icons.html;
case 'css':
return Icons.css;
case 'json':
return Icons.data_array;
case 'png':
case 'jpg':
case 'jpeg':
case 'gif':
return Icons.image;
case 'mp3':
case 'wav':
return Icons.audiotrack;
case 'mp4':
case 'avi':
case 'mov':
return Icons.videocam;
case 'zip':
case 'rar':
case '7z':
return Icons.archive;
default:
return Icons.insert_drive_file;
}
}
//
Widget get icon {
return Icon(iconData, color: isDirectory ? Colors.amber[700] : Colors.blue);
}
FileNode copyWith({
String? name,
String? path,
bool? isDirectory,
bool? isRoot,
int? depth, // depth参数
List<FileNode>? children,
bool? isExpanded,
}) {
return FileNode(
name: name ?? this.name,
path: path ?? this.path,
isDirectory: isDirectory ?? this.isDirectory,
isRoot: isRoot ?? this.isRoot,
depth: depth ?? this.depth, // depth或使用新值
children: children ?? this.children,
isExpanded: isExpanded ?? this.isExpanded,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is FileNode &&
other.name == name &&
other.path == path &&
other.isDirectory == isDirectory &&
other.isRoot == isRoot &&
listEquals(other.children, children) &&
other.isExpanded == isExpanded;
}
@override
int get hashCode {
return name.hashCode ^
path.hashCode ^
isDirectory.hashCode ^
isRoot.hashCode ^
children.hashCode ^
isExpanded.hashCode;
}
}

97
win_text_editor/lib/app/providers/editor_provider.dart

@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:win_text_editor/app/models/editor_tab.dart';
import 'package:win_text_editor/app/services/file_service.dart';
import 'package:win_text_editor/app/services/syntax_service.dart';
class EditorProvider with ChangeNotifier {
final List<EditorTab> _openTabs = [];
int _activeTabIndex = 0;
int _currentLayout = 0; // 0=, 1=, 2=
List<EditorTab> get openTabs => _openTabs;
int get activeTabIndex => _activeTabIndex;
int get currentLayout => _currentLayout;
Future<void> openFile(String filePath) async {
try {
//
final existingIndex = _openTabs.indexWhere((tab) => tab.path == filePath);
if (existingIndex != -1) {
_activeTabIndex = existingIndex;
notifyListeners();
return;
}
//
final content = await FileService.readFile(filePath);
final fileName = filePath.split('/').last;
final fileType = SyntaxService.detectFileType(fileName);
//
final newTab = EditorTab(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: fileName,
path: filePath,
content: content,
fileType: fileType,
);
_openTabs.add(newTab);
_activeTabIndex = _openTabs.length - 1;
notifyListeners();
} catch (e) {
debugPrint('Error opening file: $e');
}
}
void closeTab(String tabId) {
final index = _openTabs.indexWhere((tab) => tab.id == tabId);
if (index != -1) {
_openTabs.removeAt(index);
if (_activeTabIndex >= index && _activeTabIndex > 0) {
_activeTabIndex--;
}
notifyListeners();
}
}
void setActiveTab(int index) {
if (index >= 0 && index < _openTabs.length) {
_activeTabIndex = index;
notifyListeners();
}
}
Future<void> saveFile(String tabId) async {
final index = _openTabs.indexWhere((tab) => tab.id == tabId);
if (index != -1) {
final tab = _openTabs[index];
await FileService.writeFile(tab.path, tab.content);
//
}
}
Future<void> copyToClipboard(String tabId) async {
final index = _openTabs.indexWhere((tab) => tab.id == tabId);
if (index != -1) {
final tab = _openTabs[index];
//
// 使 clipboard package
}
}
void changeLayout(int layout) {
if (layout >= 0 && layout <= 2) {
_currentLayout = layout;
notifyListeners();
}
}
void updateTabContent(String tabId, String newContent) {
final index = _openTabs.indexWhere((tab) => tab.id == tabId);
if (index != -1) {
_openTabs[index].content = newContent;
notifyListeners();
}
}
}

227
win_text_editor/lib/app/providers/file_provider.dart

@ -0,0 +1,227 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:win_text_editor/app/models/file_node.dart';
import 'package:win_text_editor/app/services/file_service.dart';
class FileProvider with ChangeNotifier {
List<FileNode> _fileNodes = [];
bool _isLoading = false;
String _searchQuery = '';
String? _currentRootPath; //
bool get isLoading => _isLoading;
bool get hasRoot => _fileNodes.isNotEmpty && _fileNodes[0].isRoot;
// _initFileTree调用
FileProvider();
//
Future<void> setRootPath(String path) async {
_currentRootPath = path;
await _loadRootDirectory();
}
List<FileNode> get fileNodes =>
_searchQuery.isEmpty ? _fileNodes : _fileNodes.where((node) => _filterNode(node)).toList();
bool _filterNode(FileNode node) {
if (node.name.toLowerCase().contains(_searchQuery.toLowerCase())) {
return true;
}
return node.children.any(_filterNode);
}
void searchFiles(String query) {
_searchQuery = query;
notifyListeners();
}
void toggleExpand(FileNode node) {
node.isExpanded = !node.isExpanded;
notifyListeners();
}
Future<void> pickAndOpenFile() async {
final result = await FilePicker.platform.pickFiles();
if (result != null && result.files.single.path != null) {
//
debugPrint('File selected: ${result.files.single.path}');
}
}
Future<void> loadDirectory(String path) async {
_isLoading = true;
notifyListeners();
try {
final directory = Directory(path);
final rootNode = FileNode(
name: directory.path.split(Platform.pathSeparator).last,
path: directory.path,
isDirectory: true,
isRoot: true, //
children: await FileService.buildFileTree(directory.path),
);
_fileNodes = [rootNode]; //
} catch (e) {
debugPrint('Error loading directory: $e');
_fileNodes = [];
}
_isLoading = false;
notifyListeners();
}
Future<void> _loadRootDirectory() async {
if (_currentRootPath == null) return;
_isLoading = true;
notifyListeners();
try {
_fileNodes = [
FileNode(
name: _currentRootPath!.split(Platform.pathSeparator).last,
path: _currentRootPath!,
isDirectory: true,
isRoot: true,
depth: 0, // 0
children: [], //
),
];
} catch (e) {
debugPrint('Error loading root directory: $e');
_fileNodes = [];
}
_isLoading = false;
notifyListeners();
}
Future<void> loadRootDirectory(String path) async {
_isLoading = true;
notifyListeners();
try {
_fileNodes = [
FileNode(
name: path.split(Platform.pathSeparator).last,
path: path,
isDirectory: true,
isRoot: true,
children: [], //
),
];
} catch (e) {
debugPrint('Error loading root: $e');
_fileNodes = [];
}
_isLoading = false;
notifyListeners();
}
Future<void> toggleDirectory(FileNode dirNode) async {
if (dirNode.children.isEmpty) {
//
_isLoading = true;
notifyListeners();
try {
dirNode.children = await FileService.listDirectory(dirNode.path);
dirNode.isExpanded = true;
} catch (e) {
debugPrint('Error loading dir: $e');
dirNode.children = [];
}
_isLoading = false;
notifyListeners();
} else {
//
dirNode.isExpanded = !dirNode.isExpanded;
notifyListeners();
}
}
Future<void> loadDirectoryContents(FileNode dirNode) async {
if (dirNode.children.isNotEmpty && dirNode.isExpanded) {
//
dirNode.isExpanded = !dirNode.isExpanded;
notifyListeners();
return;
}
_isLoading = true;
notifyListeners();
try {
final contents = await FileService.listDirectory(
dirNode.path,
parentDepth: dirNode.depth, //
);
final updatedNode = dirNode.copyWith(
children: contents,
isExpanded: true,
// depth copyWith
);
_replaceNodeInTree(dirNode, updatedNode);
} catch (e) {
debugPrint('Error loading directory contents: $e');
final updatedNode = dirNode.copyWith(children: []);
_replaceNodeInTree(dirNode, updatedNode);
}
_isLoading = false;
notifyListeners();
}
void _replaceNodeInTree(FileNode oldNode, FileNode newNode) {
for (int i = 0; i < _fileNodes.length; i++) {
if (_fileNodes[i] == oldNode) {
_fileNodes[i] = newNode;
return;
}
_replaceNodeInChildren(_fileNodes[i], oldNode, newNode);
}
}
void _replaceNodeInChildren(FileNode parent, FileNode oldNode, FileNode newNode) {
for (int i = 0; i < parent.children.length; i++) {
if (parent.children[i] == oldNode) {
parent.children[i] = newNode;
return;
}
_replaceNodeInChildren(parent.children[i], oldNode, newNode);
}
}
Future<void> refreshFileTree({bool loadContent = false}) async {
_isLoading = true;
notifyListeners();
try {
final rootDir = await getApplicationDocumentsDirectory();
_fileNodes = [
FileNode(
name: rootDir.path.split(Platform.pathSeparator).last,
path: rootDir.path,
isDirectory: true,
isRoot: true,
//
children: loadContent ? await FileService.listDirectory(rootDir.path) : [],
),
];
} catch (e) {
debugPrint('Error refreshing file tree: $e');
_fileNodes = [];
}
_isLoading = false;
notifyListeners();
}
}

60
win_text_editor/lib/app/services/file_service.dart

@ -0,0 +1,60 @@
import 'dart:io';
import 'package:win_text_editor/app/models/file_node.dart';
class FileService {
///
static Future<List<FileNode>> listDirectory(String path, {int parentDepth = 0}) async {
final directory = Directory(path);
final List<FileNode> nodes = [];
if (await directory.exists()) {
final entities = directory.listSync();
for (final entity in entities) {
nodes.add(
FileNode(
name: entity.path.split(Platform.pathSeparator).last,
path: entity.path,
isDirectory: entity is Directory,
depth: parentDepth + 1, // +1
children: [],
),
);
}
}
return nodes;
}
///
static Future<List<FileNode>> buildFileTree(String rootPath) async {
final rootDirectory = Directory(rootPath);
final List<FileNode> nodes = [];
if (await rootDirectory.exists()) {
final entities = rootDirectory.listSync();
for (final entity in entities) {
final node = FileNode(
name: entity.path.split(Platform.pathSeparator).last,
path: entity.path,
isDirectory: entity is Directory,
);
if (entity is Directory) {
node.children.addAll(await buildFileTree(entity.path));
}
nodes.add(node);
}
}
return nodes;
}
static Future<String> readFile(String filePath) async {
return await File(filePath).readAsString();
}
static Future<void> writeFile(String filePath, String content) async {
await File(filePath).writeAsString(content);
}
}

31
win_text_editor/lib/app/services/syntax_service.dart

@ -0,0 +1,31 @@
class SyntaxService {
static String detectFileType(String fileName) {
final extension = fileName.split('.').last.toLowerCase();
switch (extension) {
case 'dart':
return 'dart';
case 'java':
return 'java';
case 'js':
return 'javascript';
case 'py':
return 'python';
case 'html':
return 'html';
case 'css':
return 'css';
case 'json':
return 'json';
case 'xml':
return 'xml';
case 'md':
return 'markdown';
case 'yaml':
case 'yml':
return 'yaml';
default:
return 'text';
}
}
}

97
win_text_editor/lib/app/widgets/editor_pane.dart

@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/editor_provider.dart';
import 'package:win_text_editor/app/widgets/tab_bar.dart';
import '../models/editor_tab.dart';
class EditorPane extends StatelessWidget {
const EditorPane({super.key});
@override
Widget build(BuildContext context) {
final editorProvider = Provider.of<EditorProvider>(context);
return Column(
children: [
const EditorTabBar(),
Expanded(
child: IndexedStack(
index: editorProvider.currentLayout,
children: [
//
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: editorProvider.openTabs.length,
itemBuilder: (context, index) {
return EditorTabContent(tab: editorProvider.openTabs[index]);
},
),
//
Stack(
children:
editorProvider.openTabs.map((tab) {
return Positioned(
top: 20.0 * editorProvider.openTabs.indexOf(tab),
left: 20.0 * editorProvider.openTabs.indexOf(tab),
right: 20.0 * editorProvider.openTabs.indexOf(tab),
bottom: 20.0 * editorProvider.openTabs.indexOf(tab),
child: EditorTabContent(tab: tab),
);
}).toList(),
),
//
if (editorProvider.openTabs.isNotEmpty)
EditorTabContent(tab: editorProvider.openTabs[editorProvider.activeTabIndex]),
],
),
),
],
);
}
}
class EditorTabContent extends StatelessWidget {
final EditorTab tab;
const EditorTabContent({super.key, required this.tab});
@override
Widget build(BuildContext context) {
final editorProvider = Provider.of<EditorProvider>(context, listen: false);
return Card(
margin: const EdgeInsets.all(8.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(tab.title),
const Spacer(),
IconButton(
icon: const Icon(Icons.content_copy),
onPressed: () => editorProvider.copyToClipboard(tab.id),
),
IconButton(
icon: const Icon(Icons.save),
onPressed: () => editorProvider.saveFile(tab.id),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => editorProvider.closeTab(tab.id),
),
],
),
),
Expanded(
child: SingleChildScrollView(
child: Container(padding: const EdgeInsets.all(8.0), child: Text(tab.content)),
),
),
],
),
);
}
}

207
win_text_editor/lib/app/widgets/file_explorer.dart

@ -0,0 +1,207 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/file_node.dart';
import '../providers/file_provider.dart';
import '../providers/editor_provider.dart';
class FileExplorer extends StatefulWidget {
const FileExplorer({super.key});
@override
State<FileExplorer> createState() => _FileExplorerState();
}
class _FileExplorerState extends State<FileExplorer> {
final ScrollController _scrollController = ScrollController(); //
@override
void dispose() {
_scrollController.dispose(); //
super.dispose();
}
@override
void initState() {
super.initState();
}
Future<void> _promptForDirectory(BuildContext context) async {
final fileProvider = Provider.of<FileProvider>(context, listen: false);
final String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
if (selectedDirectory != null) {
await fileProvider.setRootPath(selectedDirectory);
}
}
@override
Widget build(BuildContext context) {
final fileProvider = Provider.of<FileProvider>(context);
return Container(
color: Colors.grey[200],
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
hintText: 'Search files...',
prefixIcon: Icon(Icons.search),
isDense: true,
),
onChanged: (value) => fileProvider.searchFiles(value),
),
),
IconButton(
icon: const Icon(Icons.folder_open), //
onPressed: () => _promptForDirectory(context),
),
],
),
),
Expanded(
child: Scrollbar(
controller: _scrollController, //
thumbVisibility: true, //
interactive: true, //
child:
fileProvider.isLoading
? const Center(child: CircularProgressIndicator())
: fileProvider.fileNodes.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('No directory selected'),
TextButton(
onPressed: () => _promptForDirectory(context),
child: const Text('Open Folder'),
),
],
),
)
: ListView.builder(
controller: _scrollController, // ListView使用相同的控制器
itemCount: _countVisibleNodes(fileProvider.fileNodes),
itemBuilder: (context, index) {
final node = _getVisibleNode(fileProvider.fileNodes, index);
return _FileNodeWidget(
key: ValueKey(node.path),
node: node,
onTap: () async {
if (node.isDirectory) {
await fileProvider.loadDirectoryContents(node);
}
},
);
},
),
),
),
],
),
);
}
}
//
int _countVisibleNodes(List<FileNode> nodes) {
int count = 0;
for (final node in nodes) {
count++;
if (node.isDirectory && node.isExpanded) {
count += _countVisibleNodes(node.children);
}
}
return count;
}
//
FileNode _getVisibleNode(List<FileNode> nodes, int index) {
int current = 0;
for (final node in nodes) {
if (current == index) return node;
current++;
if (node.isDirectory && node.isExpanded) {
final childCount = _countVisibleNodes(node.children);
if (index - current < childCount) {
return _getVisibleNode(node.children, index - current);
}
current += childCount;
}
}
throw Exception('Index out of bounds: $index (max: ${current - 1})');
}
class _FileNodeWidget extends StatelessWidget {
final FileNode node;
final VoidCallback onTap;
const _FileNodeWidget({
Key? key, // key
required this.node,
required this.onTap,
}) : super(key: key); //
@override
Widget build(BuildContext context) {
final editorProvider = Provider.of<EditorProvider>(context, listen: false);
return InkWell(
onTap: () {
if (node.isDirectory) {
onTap();
} else {
editorProvider.openFile(node.path);
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 0), //
child: ListTile(
dense: true, //
visualDensity: const VisualDensity(
vertical: -4, //
),
contentPadding: const EdgeInsets.symmetric(horizontal: 2), //
minVerticalPadding: 0, // 0
leading: _buildLeadingWidget(context),
title: Text(
node.name,
style: Theme.of(context).textTheme.bodyMedium, // 使
),
),
),
);
}
Widget _buildLeadingWidget(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
// Indentation lines
...List.generate(node.depth, (index) {
return Padding(
padding: const EdgeInsets.only(left: 6, right: 6.0),
child: Container(width: 1.0, height: 32.0, color: Colors.grey[500]),
);
}),
node.isDirectory
? Icon(
node.isExpanded ? Icons.expand_more : Icons.chevron_right,
color: Colors.cyan[200],
size: 20, //
)
: const Icon(null, size: 2),
node.isDirectory
? Icon(Icons.folder, color: Colors.cyan[500], size: 16)
: Icon(Icons.file_open, color: Colors.amber[700], size: 16),
],
);
}
}

83
win_text_editor/lib/app/widgets/tab_bar.dart

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/editor_provider.dart';
import '../providers/file_provider.dart';
class EditorTabBar extends StatelessWidget {
const EditorTabBar({super.key});
@override
Widget build(BuildContext context) {
final editorProvider = Provider.of<EditorProvider>(context);
return Container(
height: 40,
color: Colors.grey[300],
child: Row(
children: [
//
PopupMenuButton<int>(
icon: const Icon(Icons.grid_view),
itemBuilder:
(context) => [
const PopupMenuItem(value: 0, child: Text('平铺布局')),
const PopupMenuItem(value: 1, child: Text('层叠布局')),
const PopupMenuItem(value: 2, child: Text('单页布局')),
],
onSelected: (value) => editorProvider.changeLayout(value),
),
//
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: editorProvider.openTabs.length,
itemBuilder: (context, index) {
final tab = editorProvider.openTabs[index];
return InkWell(
onTap: () => editorProvider.setActiveTab(index),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: BoxDecoration(
color:
editorProvider.activeTabIndex == index ? Colors.white : Colors.grey[200],
border: Border(
bottom: BorderSide(
color:
editorProvider.activeTabIndex == index
? Colors.blue
: Colors.transparent,
width: 2.0,
),
),
),
child: Center(
child: Row(
children: [
Text(tab.title),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.close, size: 16),
onPressed: () => editorProvider.closeTab(tab.id),
),
],
),
),
),
);
},
),
),
//
IconButton(
icon: const Icon(Icons.add),
onPressed: () async {
final fileProvider = Provider.of<FileProvider>(context, listen: false);
await fileProvider.pickAndOpenFile();
},
),
],
),
);
}
}

128
win_text_editor/lib/main.dart

@ -1,122 +1,38 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';
import 'app/app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
size: Size(1200, 800),
center: true,
title: 'Win Text Editor',
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
void main() {
runApp(const MyApp()); runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
// This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Demo', title: '文本编辑器',
debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(
// This is the theme of your application. primarySwatch: Colors.blue,
// visualDensity: VisualDensity.adaptivePlatformDensity,
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
), ),
home: const MyHomePage(title: 'Flutter Demo Home Page'), home: const AppScaffold(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
); );
} }
} }

223
win_text_editor/pubspec.lock

@ -9,6 +9,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.0" version: "2.12.0"
bitsdojo_window:
dependency: "direct main"
description:
name: bitsdojo_window
sha256: "88ef7765dafe52d97d7a3684960fb5d003e3151e662c18645c1641c22b873195"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
bitsdojo_window_linux:
dependency: transitive
description:
name: bitsdojo_window_linux
sha256: "9519c0614f98be733e0b1b7cb15b827007886f6fe36a4fb62cf3d35b9dd578ab"
url: "https://pub.dev"
source: hosted
version: "0.1.4"
bitsdojo_window_macos:
dependency: transitive
description:
name: bitsdojo_window_macos
sha256: f7c5be82e74568c68c5b8449e2c5d8fd12ec195ecd70745a7b9c0f802bb0268f
url: "https://pub.dev"
source: hosted
version: "0.1.4"
bitsdojo_window_platform_interface:
dependency: transitive
description:
name: bitsdojo_window_platform_interface
sha256: "65daa015a0c6dba749bdd35a0f092e7a8ba8b0766aa0480eb3ef808086f6e27c"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
bitsdojo_window_windows:
dependency: transitive
description:
name: bitsdojo_window_windows
sha256: fa982cf61ede53f483e50b257344a1c250af231a3cdc93a7064dd6dc0d720b68
url: "https://pub.dev"
source: hosted
version: "0.1.6"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -41,14 +81,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
cupertino_icons: cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
expandable:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: expandable
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "5.0.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -57,6 +105,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.2"
ffi:
dependency: transitive
description:
name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "978be1f602e0695daef8e345a3c597abf72b0c0ca6102fa2665eb549f5406a17"
url: "https://pub.dev"
source: hosted
version: "10.1.5"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -66,15 +130,36 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
url: "https://pub.dev"
source: hosted
version: "2.0.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
url: "https://pub.dev"
source: hosted
version: "2.0.28"
flutter_syntax_view:
dependency: "direct main"
description:
name: flutter_syntax_view
sha256: c5017bbedfdcf538daba765e16541fcb26434071655ca00cea7cbc205a70246a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "4.1.7"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -103,10 +188,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "2.1.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -131,6 +216,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -139,6 +232,86 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
provider:
dependency: "direct main"
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
screen_retriever:
dependency: transitive
description:
name: screen_retriever
sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90"
url: "https://pub.dev"
source: hosted
version: "0.1.9"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -208,6 +381,38 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" version: "14.3.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
url: "https://pub.dev"
source: hosted
version: "5.12.0"
window_manager:
dependency: "direct main"
description:
name: window_manager
sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf"
url: "https://pub.dev"
source: hosted
version: "0.3.9"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
sdks: sdks:
dart: ">=3.7.0 <4.0.0" dart: ">=3.7.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54" flutter: ">=3.27.0"

84
win_text_editor/pubspec.yaml

@ -1,89 +1,29 @@
name: win_text_editor name: win_text_editor
description: "A new Flutter project." description: A Windows text editor application built with Flutter
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ^3.7.0 sdk: '>=3.7.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
provider: ^6.1.5
path_provider: ^2.1.5
file_picker: ^10.1.5
# flutter_highlight: ^0.7.0
# highlight: ^0.7.0
window_manager: ^0.3.1
bitsdojo_window: ^0.1.1+2
flutter_syntax_view: ^4.1.7
expandable: ^5.0.1
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter: flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package

9
win_text_editor/windows/flutter/generated_plugin_registrant.cc

@ -6,6 +6,15 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
BitsdojoWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
ScreenRetrieverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
} }

3
win_text_editor/windows/flutter/generated_plugins.cmake

@ -3,6 +3,9 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_windows
screen_retriever
window_manager
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

Loading…
Cancel
Save