some fix, add use libass feature
BIN
assets/fonts/subfont.ttf
Normal file
|
|
@ -323,5 +323,8 @@
|
|||
"grid_size": "Grid size",
|
||||
"n_per_row": "{n} per row",
|
||||
"horizontal_continious": "Horizontal continuous",
|
||||
"edit_code": "Edit code"
|
||||
"edit_code": "Edit code",
|
||||
"use_libass": "Enable libass",
|
||||
"use_libass_info": "Use libass based subtitle rendering for native backend.",
|
||||
"libass_not_disable_message": "Disable `use libass` in player settings to be able to customize the subtitles."
|
||||
}
|
||||
|
|
@ -186,6 +186,8 @@ class Settings {
|
|||
@enumerated
|
||||
late SectionType disableSectionType;
|
||||
|
||||
bool? useLibass;
|
||||
|
||||
Settings(
|
||||
{this.id = 227,
|
||||
this.displayType = DisplayType.compactGrid,
|
||||
|
|
@ -267,7 +269,8 @@ class Settings {
|
|||
this.appFontFamily,
|
||||
this.mangaGridSize,
|
||||
this.animeGridSize,
|
||||
this.disableSectionType = SectionType.all});
|
||||
this.disableSectionType = SectionType.all,
|
||||
this.useLibass = true});
|
||||
|
||||
Settings.fromJson(Map<String, dynamic> json) {
|
||||
animatePageTransitions = json['animatePageTransitions'];
|
||||
|
|
@ -416,6 +419,7 @@ class Settings {
|
|||
animeGridSize = json['animeGridSize'];
|
||||
disableSectionType =
|
||||
SectionType.values[json['disableSectionType'] ?? SectionType.all];
|
||||
useLibass = json['useLibass'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
@ -523,7 +527,8 @@ class Settings {
|
|||
'appFontFamily': appFontFamily,
|
||||
'mangaGridSize': mangaGridSize,
|
||||
'animeGridSize': animeGridSize,
|
||||
'disableSectionType': disableSectionType.index
|
||||
'disableSectionType': disableSectionType.index,
|
||||
'useLibass': useLibass
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -451,13 +451,18 @@ const SettingsSchema = CollectionSchema(
|
|||
name: r'updateProgressAfterReading',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'usePageTapZones': PropertySchema(
|
||||
r'useLibass': PropertySchema(
|
||||
id: 82,
|
||||
name: r'useLibass',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'usePageTapZones': PropertySchema(
|
||||
id: 83,
|
||||
name: r'usePageTapZones',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'userAgent': PropertySchema(
|
||||
id: 83,
|
||||
id: 84,
|
||||
name: r'userAgent',
|
||||
type: IsarType.string,
|
||||
)
|
||||
|
|
@ -911,8 +916,9 @@ void _settingsSerialize(
|
|||
writer.writeLong(offsets[79], object.startDatebackup);
|
||||
writer.writeBool(offsets[80], object.themeIsDark);
|
||||
writer.writeBool(offsets[81], object.updateProgressAfterReading);
|
||||
writer.writeBool(offsets[82], object.usePageTapZones);
|
||||
writer.writeString(offsets[83], object.userAgent);
|
||||
writer.writeBool(offsets[82], object.useLibass);
|
||||
writer.writeBool(offsets[83], object.usePageTapZones);
|
||||
writer.writeString(offsets[84], object.userAgent);
|
||||
}
|
||||
|
||||
Settings _settingsDeserialize(
|
||||
|
|
@ -1073,8 +1079,9 @@ Settings _settingsDeserialize(
|
|||
startDatebackup: reader.readLongOrNull(offsets[79]),
|
||||
themeIsDark: reader.readBoolOrNull(offsets[80]),
|
||||
updateProgressAfterReading: reader.readBoolOrNull(offsets[81]),
|
||||
usePageTapZones: reader.readBoolOrNull(offsets[82]),
|
||||
userAgent: reader.readStringOrNull(offsets[83]),
|
||||
useLibass: reader.readBoolOrNull(offsets[82]),
|
||||
usePageTapZones: reader.readBoolOrNull(offsets[83]),
|
||||
userAgent: reader.readStringOrNull(offsets[84]),
|
||||
);
|
||||
object.chapterFilterBookmarkedList =
|
||||
reader.readObjectList<ChapterFilterBookmarked>(
|
||||
|
|
@ -1366,6 +1373,8 @@ P _settingsDeserializeProp<P>(
|
|||
case 82:
|
||||
return (reader.readBoolOrNull(offset)) as P;
|
||||
case 83:
|
||||
return (reader.readBoolOrNull(offset)) as P;
|
||||
case 84:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
|
|
@ -6805,6 +6814,32 @@ extension SettingsQueryFilter
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterFilterCondition> useLibassIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'useLibass',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterFilterCondition> useLibassIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'useLibass',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterFilterCondition> useLibassEqualTo(
|
||||
bool? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'useLibass',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterFilterCondition>
|
||||
usePageTapZonesIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
@ -8008,6 +8043,18 @@ extension SettingsQuerySortBy on QueryBuilder<Settings, Settings, QSortBy> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseLibass() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'useLibass', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseLibassDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'useLibass', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUsePageTapZones() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'usePageTapZones', Sort.asc);
|
||||
|
|
@ -8896,6 +8943,18 @@ extension SettingsQuerySortThenBy
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseLibass() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'useLibass', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseLibassDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'useLibass', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUsePageTapZones() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'usePageTapZones', Sort.asc);
|
||||
|
|
@ -9358,6 +9417,12 @@ extension SettingsQueryWhereDistinct
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QDistinct> distinctByUseLibass() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'useLibass');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, Settings, QDistinct> distinctByUsePageTapZones() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'usePageTapZones');
|
||||
|
|
@ -9928,6 +9993,12 @@ extension SettingsQueryProperty
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, bool?, QQueryOperations> useLibassProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'useLibass');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Settings, bool?, QQueryOperations> usePageTapZonesProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'usePageTapZones');
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
|||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
// ignore: depend_on_referenced_packages
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class AnimePlayerView extends riv.ConsumerStatefulWidget {
|
||||
final Chapter episode;
|
||||
|
|
@ -40,7 +43,9 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
|
|||
String? _infoHash;
|
||||
@override
|
||||
void dispose() {
|
||||
MTorrentServer().removeTorrent(_infoHash);
|
||||
if (_infoHash != null) {
|
||||
MTorrentServer().removeTorrent(_infoHash);
|
||||
}
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||
overlays: SystemUiOverlay.values);
|
||||
super.dispose();
|
||||
|
|
@ -53,8 +58,9 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
|
|||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||
return serversData.when(
|
||||
data: (data) {
|
||||
_infoHash = data.$3;
|
||||
if (data.$1.isEmpty &&
|
||||
final (videos, isLocal, infoHash) = data;
|
||||
_infoHash = infoHash;
|
||||
if (videos.isEmpty &&
|
||||
!(widget.episode.manga.value!.isLocalArchive ?? false)) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
|
|
@ -67,15 +73,15 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
|
|||
),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text("Error"),
|
||||
child: Text("Video list is empty"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AnimeStreamPage(
|
||||
episode: widget.episode,
|
||||
videos: data.$1,
|
||||
isLocal: data.$2,
|
||||
videos: videos,
|
||||
isLocal: isLocal,
|
||||
);
|
||||
},
|
||||
error: (error, stackTrace) => Scaffold(
|
||||
|
|
@ -133,19 +139,18 @@ class AnimeStreamPage extends riv.ConsumerStatefulWidget {
|
|||
class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||
with TickerProviderStateMixin {
|
||||
late final GlobalKey<VideoState> _key = GlobalKey<VideoState>();
|
||||
late final Player _player = Player();
|
||||
late final useLibass = ref.read(useLibassStateProvider);
|
||||
late final Player _player =
|
||||
Player(configuration: PlayerConfiguration(libass: useLibass));
|
||||
late final VideoController _controller = VideoController(_player);
|
||||
late final _streamController =
|
||||
ref.read(animeStreamControllerProvider(episode: widget.episode).notifier);
|
||||
late final _firstVid = widget.videos.first;
|
||||
|
||||
late final ValueNotifier<VideoPrefs?> _video = ValueNotifier(VideoPrefs(
|
||||
videoTrack: VideoTrack(
|
||||
_firstVid.originalUrl, _firstVid.quality, _firstVid.quality),
|
||||
headers: _firstVid.headers));
|
||||
|
||||
final ValueNotifier<double> _playbackSpeed = ValueNotifier(1.0);
|
||||
bool _initSubtitleAndAudio = true;
|
||||
final ValueNotifier<bool> _enterFullScreen = ValueNotifier(false);
|
||||
late final ValueNotifier<Duration> _currentPosition =
|
||||
ValueNotifier(_streamController.geTCurrentPosition());
|
||||
|
|
@ -154,6 +159,13 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
final ValueNotifier<bool> _isCompleted = ValueNotifier(false);
|
||||
final ValueNotifier<Duration?> _tempPosition = ValueNotifier(null);
|
||||
final ValueNotifier<BoxFit> _fit = ValueNotifier(BoxFit.contain);
|
||||
final ValueNotifier<bool> _showAniSkipOpeningButton = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _showAniSkipEndingButton = ValueNotifier(false);
|
||||
Results? _openingResult;
|
||||
Results? _endingResult;
|
||||
bool _hasOpeningSkip = false;
|
||||
bool _hasEndingSkip = false;
|
||||
bool _initSubtitleAndAudio = true;
|
||||
|
||||
late final StreamSubscription<Duration> _currentPositionSub =
|
||||
_player.stream.position.listen(
|
||||
|
|
@ -163,27 +175,25 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
10;
|
||||
_currentPosition.value = position;
|
||||
|
||||
if ((_firstVid.subtitles ?? []).isNotEmpty) {
|
||||
if (_firstVid.subtitles?.isNotEmpty ?? false) {
|
||||
if (_initSubtitleAndAudio) {
|
||||
try {
|
||||
if (_firstVid.subtitles?.isNotEmpty ?? false) {
|
||||
final file = _firstVid.subtitles?.first.file ?? "";
|
||||
final label = _firstVid.subtitles?.first.label;
|
||||
_player.setSubtitleTrack(file.startsWith("http")
|
||||
? SubtitleTrack.uri(file, title: label, language: label)
|
||||
: SubtitleTrack.data(file, title: label, language: label));
|
||||
}
|
||||
final file = _firstVid.subtitles!.first.file ?? "";
|
||||
final label = _firstVid.subtitles!.first.label;
|
||||
_player.setSubtitleTrack(file.startsWith("http")
|
||||
? SubtitleTrack.uri(file, title: label, language: label)
|
||||
: SubtitleTrack.data(file, title: label, language: label));
|
||||
} catch (_) {}
|
||||
try {
|
||||
if (_firstVid.audios?.isNotEmpty ?? false) {
|
||||
_player.setAudioTrack(AudioTrack.uri(
|
||||
_firstVid.audios?.first.file ?? "",
|
||||
title: _firstVid.audios?.first.label,
|
||||
language: _firstVid.audios?.first.label));
|
||||
}
|
||||
} catch (_) {}
|
||||
_initSubtitleAndAudio = false;
|
||||
}
|
||||
try {
|
||||
if (_firstVid.audios?.isNotEmpty ?? false) {
|
||||
_player.setAudioTrack(AudioTrack.uri(
|
||||
_firstVid.audios!.first.file ?? "",
|
||||
title: _firstVid.audios!.first.label,
|
||||
language: _firstVid.audios!.first.label));
|
||||
}
|
||||
} catch (_) {}
|
||||
_initSubtitleAndAudio = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -194,6 +204,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
_currentTotalDuration.value = duration;
|
||||
},
|
||||
);
|
||||
|
||||
late final StreamSubscription<bool> _completed =
|
||||
_player.stream.completed.listen(
|
||||
(val) async {
|
||||
|
|
@ -213,33 +224,48 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
}
|
||||
},
|
||||
);
|
||||
Results? _openingResult;
|
||||
Results? _endingResult;
|
||||
bool _hasOpeningSkip = false;
|
||||
bool _hasEndingSkip = false;
|
||||
final ValueNotifier<bool> _showAniSkipOpeningButton = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _showAniSkipEndingButton = ValueNotifier(false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_setCurrentPosition(true);
|
||||
_seekToCurrentPosition();
|
||||
_seekToOrCurrentPosition();
|
||||
_currentPositionSub;
|
||||
_currentTotalDurationSub;
|
||||
_completed;
|
||||
_player.open(Media(_video.value!.videoTrack!.id,
|
||||
httpHeaders: _video.value!.headers));
|
||||
_setPlaybackSpeed(ref.read(defaultPlayBackSpeedStateProvider));
|
||||
_initAniSkip();
|
||||
_loadAndroidFont().then(
|
||||
(_) {
|
||||
_player.open(Media(_video.value!.videoTrack!.id,
|
||||
httpHeaders: _video.value!.headers));
|
||||
_setPlaybackSpeed(ref.read(defaultPlayBackSpeedStateProvider));
|
||||
_initAniSkip();
|
||||
},
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _seekToCurrentPosition({Duration? duration}) async {
|
||||
await Future.doWhile(() async {
|
||||
Future<void> _loadAndroidFont() async {
|
||||
if (Platform.isAndroid && useLibass) {
|
||||
try {
|
||||
final subDir = await getApplicationDocumentsDirectory();
|
||||
final fontPath = path.join(subDir.path, 'subfont.ttf');
|
||||
final data = await rootBundle.load('assets/fonts/subfont.ttf');
|
||||
final bytes =
|
||||
data.buffer.asInt8List(data.offsetInBytes, data.lengthInBytes);
|
||||
final fontFile = await File(fontPath).create(recursive: true);
|
||||
await fontFile.writeAsBytes(bytes);
|
||||
await (_player.platform as NativePlayer)
|
||||
.setProperty('sub-fonts-dir', subDir.path);
|
||||
await (_player.platform as NativePlayer)
|
||||
.setProperty('sub-font', 'Droid Sans Fallback');
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
void _seekToOrCurrentPosition({Duration? duration}) async {
|
||||
if (duration == null) {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
await _player.stream.buffer.first;
|
||||
_player.seek(duration ?? _streamController.geTCurrentPosition());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
await _player.stream.buffer.first;
|
||||
_player.seek(duration ?? _streamController.geTCurrentPosition());
|
||||
}
|
||||
|
||||
void _initAniSkip() async {
|
||||
|
|
@ -276,7 +302,6 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
} else {
|
||||
_setLandscapeMode(false);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -346,7 +371,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
child: textWidget(
|
||||
widget.isLocal ? _firstVid.quality : quality.videoTrack!.title!,
|
||||
selected),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
_video.value = quality;
|
||||
if (quality.isLocal) {
|
||||
if (widget.isLocal) {
|
||||
|
|
@ -359,7 +384,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
_player.open(Media(quality.videoTrack!.id,
|
||||
httpHeaders: quality.headers));
|
||||
}
|
||||
_seekToCurrentPosition(duration: _currentPosition.value);
|
||||
_seekToOrCurrentPosition(duration: _currentPosition.value);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
|
|
@ -388,15 +413,25 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
fullWidth: true,
|
||||
moreWidget: IconButton(
|
||||
onPressed: () async {
|
||||
await customDraggableTabBar(tabs: [
|
||||
Tab(text: l10n.font),
|
||||
Tab(text: l10n.color),
|
||||
], children: [
|
||||
FontSettingWidget(hasSubtitleTrack: hasSubtitleTrack),
|
||||
ColorSettingWidget(hasSubtitleTrack: hasSubtitleTrack)
|
||||
], context: context, vsync: this, fullWidth: true);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
if (useLibass) {
|
||||
BotToast.showText(
|
||||
contentColor: Colors.white,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 20),
|
||||
onlyOne: true,
|
||||
align: const Alignment(0, 0.90),
|
||||
duration: const Duration(seconds: 2),
|
||||
text: context.l10n.libass_not_disable_message);
|
||||
} else {
|
||||
await customDraggableTabBar(tabs: [
|
||||
Tab(text: l10n.font),
|
||||
Tab(text: l10n.color),
|
||||
], children: [
|
||||
FontSettingWidget(hasSubtitleTrack: hasSubtitleTrack),
|
||||
ColorSettingWidget(hasSubtitleTrack: hasSubtitleTrack)
|
||||
], context: context, vsync: this, fullWidth: true);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.settings_outlined)),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:mangayomi/modules/anime/anime_player_view.dart';
|
|||
import 'package:mangayomi/modules/anime/providers/anime_player_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/anime/widgets/custom_seekbar.dart';
|
||||
import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
||||
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
|
@ -192,12 +193,14 @@ class _DesktopControllerWidgetState extends State<DesktopControllerWidget> {
|
|||
child: Stack(
|
||||
children: [
|
||||
Consumer(
|
||||
builder: (context, ref, _) => Positioned(
|
||||
child: CustomSubtitleView(
|
||||
controller: widget.videoController,
|
||||
configuration:
|
||||
SubtitleViewConfiguration(style: subtileTextStyle(ref)),
|
||||
)),
|
||||
builder: (context, ref, _) => ref.read(useLibassStateProvider)
|
||||
? const SizedBox.shrink()
|
||||
: Positioned(
|
||||
child: CustomSubtitleView(
|
||||
controller: widget.videoController,
|
||||
configuration:
|
||||
SubtitleViewConfiguration(style: subtileTextStyle(ref)),
|
||||
)),
|
||||
),
|
||||
Focus(
|
||||
autofocus: true,
|
||||
|
|
@ -815,26 +818,15 @@ Future<bool> setFullScreen({bool? value}) async {
|
|||
if (value != null) {
|
||||
final isFullScreen = await windowManager.isFullScreen();
|
||||
if (value != isFullScreen) {
|
||||
await windowManager.setTitleBarStyle(
|
||||
value == false ? TitleBarStyle.normal : TitleBarStyle.hidden);
|
||||
await windowManager.setFullScreen(value);
|
||||
if (value == false) {
|
||||
await windowManager.center();
|
||||
}
|
||||
await windowManager.show();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
final isFullScreen = await windowManager.isFullScreen();
|
||||
if (!isFullScreen) {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
||||
await windowManager.setFullScreen(true);
|
||||
await windowManager.show();
|
||||
} else {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
||||
await windowManager.setFullScreen(false);
|
||||
await windowManager.center();
|
||||
await windowManager.show();
|
||||
}
|
||||
return isFullScreen;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,12 +289,14 @@ class _MobileControllerWidgetState
|
|||
return Stack(
|
||||
children: [
|
||||
Consumer(
|
||||
builder: (context, ref, _) => Positioned(
|
||||
child: CustomSubtitleView(
|
||||
controller: widget.videoController,
|
||||
configuration:
|
||||
SubtitleViewConfiguration(style: subtileTextStyle(ref)),
|
||||
)),
|
||||
builder: (context, ref, _) => ref.read(useLibassStateProvider)
|
||||
? const SizedBox.shrink()
|
||||
: Positioned(
|
||||
child: CustomSubtitleView(
|
||||
controller: widget.videoController,
|
||||
configuration:
|
||||
SubtitleViewConfiguration(style: subtileTextStyle(ref)),
|
||||
)),
|
||||
),
|
||||
Focus(
|
||||
autofocus: true,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import 'package:path_provider/path_provider.dart';
|
|||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'download_provider.g.dart';
|
||||
|
||||
FileDownloader _mangaFileDownloader = FileDownloader(isAnime: false);
|
||||
FileDownloader _animeFileDownloader = FileDownloader(isAnime: true);
|
||||
@riverpod
|
||||
Future<List<String>> downloadChapter(
|
||||
DownloadChapterRef ref, {
|
||||
|
|
@ -232,9 +234,8 @@ Future<List<String>> downloadChapter(
|
|||
isar.downloads.putSync(download..chapter.value = chapter);
|
||||
});
|
||||
} else {
|
||||
savePageUrls();
|
||||
if (isManga) {
|
||||
await FileDownloader(isAnime: false).downloadBatch(
|
||||
await _mangaFileDownloader.downloadBatch(
|
||||
tasks,
|
||||
batchProgressCallback: (succeeded, failed) async {
|
||||
if (succeeded == tasks.length) {
|
||||
|
|
@ -287,7 +288,7 @@ Future<List<String>> downloadChapter(
|
|||
},
|
||||
);
|
||||
} else {
|
||||
await FileDownloader(isAnime: true).download(
|
||||
await _animeFileDownloader.download(
|
||||
tasks.first,
|
||||
onProgress: (progress) async {
|
||||
bool isEmpty = isar.downloads
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'download_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$downloadChapterHash() => r'de1315d4bfaf41676c567c07d1e0ed816b7d569e';
|
||||
String _$downloadChapterHash() => r'55ee5ac033e4616be7fbf040a7396a2f9d36599d';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'crop_borders_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$cropBordersHash() => r'2f295179ecab0d811728451a2dbc8c61121f50e3';
|
||||
String _$cropBordersHash() => r'857ae4319c11ee55cbaa2ae15b57bda240329c0d';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -13,15 +13,13 @@ class PlayerScreen extends ConsumerWidget {
|
|||
final markEpisodeAsSeenType = ref.watch(markEpisodeAsSeenTypeStateProvider);
|
||||
final defaultSkipIntroLength =
|
||||
ref.watch(defaultSkipIntroLengthStateProvider);
|
||||
|
||||
final defaultDoubleTapToSkipLength =
|
||||
ref.watch(defaultDoubleTapToSkipLengthStateProvider);
|
||||
final defaultPlayBackSpeed = ref.watch(defaultPlayBackSpeedStateProvider);
|
||||
|
||||
final enableAniSkip = ref.watch(enableAniSkipStateProvider);
|
||||
final enableAutoSkip = ref.watch(enableAutoSkipStateProvider);
|
||||
final aniSkipTimeoutLength = ref.watch(aniSkipTimeoutLengthStateProvider);
|
||||
|
||||
final useLibass = ref.watch(useLibassStateProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.l10n.player),
|
||||
|
|
@ -285,9 +283,17 @@ class PlayerScreen extends ConsumerWidget {
|
|||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: useLibass,
|
||||
title: Text(context.l10n.use_libass),
|
||||
subtitle: Text(context.l10n.use_libass_info,
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
onChanged: (value) {
|
||||
ref.read(useLibassStateProvider.notifier).set(value);
|
||||
}),
|
||||
ExpansionTile(
|
||||
title: Text(context.l10n.enable_aniskip),
|
||||
shape: const StarBorder(),
|
||||
initiallyExpanded: enableAniSkip,
|
||||
trailing: IgnorePointer(
|
||||
child: Switch(
|
||||
|
|
|
|||
|
|
@ -108,3 +108,18 @@ class AniSkipTimeoutLengthState extends _$AniSkipTimeoutLengthState {
|
|||
() => isar.settings.putSync(settings!..aniSkipTimeoutLength = value));
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class UseLibassState extends _$UseLibassState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.useLibass ?? true;
|
||||
}
|
||||
|
||||
void set(bool value) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = value;
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(settings!..useLibass = value));
|
||||
}
|
||||
}
|
||||
|
|
@ -125,5 +125,21 @@ final aniSkipTimeoutLengthStateProvider =
|
|||
);
|
||||
|
||||
typedef _$AniSkipTimeoutLengthState = AutoDisposeNotifier<int>;
|
||||
String _$useLibassStateHash() => r'09c661f72c8777f360f48f2203d767b9caf6e4e7';
|
||||
|
||||
/// See also [UseLibassState].
|
||||
@ProviderFor(UseLibassState)
|
||||
final useLibassStateProvider =
|
||||
AutoDisposeNotifierProvider<UseLibassState, bool>.internal(
|
||||
UseLibassState.new,
|
||||
name: r'useLibassStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$useLibassStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$UseLibassState = AutoDisposeNotifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
## 0.2.3
|
||||
|
||||
* fix macOS webview window size not working.
|
||||
|
||||
## 0.2.2
|
||||
|
||||
* fix memory leak on macOS after close webview window.
|
||||
* Show and Hide Webview window by [@Iri-Hor](https://github.com/Iri-Hor) in [#268](https://github.com/MixinNetwork/flutter-plugins/pull/268)
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* add Windows attentions to readme.
|
||||
* fix linux close sub window cause app exited.
|
||||
* fix linux webview title bar expanded unexpected.
|
||||
* More control over webview position and size under windows. [#206](https://github.com/MixinNetwork/flutter-plugins/pull/206) by [Lukas Heinze](https://github.com/Iri-Hor)
|
||||
* fix zone mismatch [#250](https://github.com/MixinNetwork/flutter-plugins/pull/250) by [CD](https://github.com/459217974)
|
||||
* fix linux webkit2gtk deprecated error [#246](https://github.com/MixinNetwork/flutter-plugins/pull/246) by [Zhiqiang Zhang](https://github.com/zhangzqs)
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* BREAK CHANGE: bump linux webkit2gtk version to 4.1
|
||||
|
||||
## 0.1.6
|
||||
|
||||
* fix WebView render area wrong offset on Windows.
|
||||
|
||||
## 0.1.5
|
||||
|
||||
* add `close` method for WebView.
|
||||
* add `onUrlRequest` event for WebView.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
* support custom userDataFolder on Windows.
|
||||
* fix open web view failed cause crash on Windows.
|
||||
|
||||
## 0.1.3
|
||||
|
||||
Remove windows addition import requirements.
|
||||
|
||||
## 0.1.2
|
||||
|
||||
fix TitleBar reload do not work.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
fix window title not show on macOS
|
||||
|
||||
## 0.1.0
|
||||
|
||||
support custom titlebar.
|
||||
|
||||
NOTE: contains break change. more details see readme.
|
||||
|
||||
## 0.0.7
|
||||
|
||||
1. support `isWebviewAvailable` check.
|
||||
2. fix `clearAll` crash on Linux if no webview created.
|
||||
|
||||
## 0.0.6
|
||||
|
||||
fix swift definition conflict on macOS. [flutter-plugins#17](https://github.com/MixinNetwork/flutter-plugins/issues/17)
|
||||
|
||||
## 0.0.5
|
||||
|
||||
add `setApplicationNameForUserAgent` for append application name to webview user agent.
|
||||
|
||||
## 0.0.4
|
||||
|
||||
1. implement `addScriptToExecuteOnDocumentCreated` on macOS.
|
||||
2. add hot key `command + w` to close window.
|
||||
|
||||
## 0.0.3
|
||||
|
||||
fix linux build
|
||||
|
||||
## 0.0.2
|
||||
|
||||
* rename project to desktop_webview_window
|
||||
|
||||
## 0.0.1
|
||||
|
||||
* add Windows, Linux, macOS support.
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2021] [Mixin]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
# desktop_webview_window
|
||||
|
||||
[](https://pub.dev/packages/desktop_webview_window)
|
||||
|
||||
Show a webview window on your flutter desktop application.
|
||||
|
||||
| | | |
|
||||
| -------- | ------- | ---- |
|
||||
| Windows | ✅ | [Webview2](https://www.nuget.org/packages/Microsoft.Web.WebView2) 1.0.992.28 |
|
||||
| Linux | ✅ | [WebKitGTK-4.1](https://webkitgtk.org/reference/webkit2gtk/stable/index.html) |
|
||||
| macOS | ✅ | WKWebview |
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. modify your `main` method.
|
||||
```dart
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Add this your main method.
|
||||
// used to show a webview title bar.
|
||||
if (runWebViewTitleBarWidget(args)) {
|
||||
return;
|
||||
}
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
2. launch WebViewWindow
|
||||
|
||||
```dart
|
||||
final webview = await WebviewWindow.create();
|
||||
webview.launch("https://example.com");
|
||||
```
|
||||
|
||||
## linux requirement
|
||||
|
||||
```shell
|
||||
sudo apt-get install webkit2gtk-4.1
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
### Requirement
|
||||
|
||||
The backend of desktop_webview_window on Windows is WebView2, which requires **WebView2 Runtime** installed.
|
||||
|
||||
[WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2) is ship in box with Windows11, but
|
||||
it may not installed on Windows10 devices. So you need consider how to distribute the runtime to your users.
|
||||
|
||||
See more: https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution
|
||||
|
||||
For convenience, you can use `WebviewWindow.isWebviewAvailable()` check whether the WebView2 is available.
|
||||
|
||||
### Attention
|
||||
|
||||
The default user data folder of WebView2 is `your_exe_file\WebView2`, which is not a good place to store user data.
|
||||
|
||||
eg. if the application is installed in a read-only directory, the application will crash when WebView2 try to write data.
|
||||
|
||||
you can use `WebviewWindow.create()` to create a webview with a custom user data folder.
|
||||
|
||||
```dart
|
||||
final webview = await WebviewWindow.create(
|
||||
confiruation: CreateConfiguration(
|
||||
userDataFolderWindows: 'your_custom_user_data_folder',
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
see [LICENSE](./LICENSE)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# webview_window_example
|
||||
|
||||
Demonstrates how to use the webview_window plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
void main(List<String> args) {
|
||||
debugPrint('args: $args');
|
||||
if (runWebViewTitleBarWidget(args)) {
|
||||
return;
|
||||
}
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
final TextEditingController _controller = TextEditingController(
|
||||
text: 'https://example.com',
|
||||
);
|
||||
|
||||
bool? _webviewAvailable;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WebviewWindow.isWebviewAvailable().then((value) {
|
||||
setState(() {
|
||||
_webviewAvailable = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final webview = await WebviewWindow.create(
|
||||
configuration: CreateConfiguration(
|
||||
windowHeight: 1280,
|
||||
windowWidth: 720,
|
||||
title: "ExampleTestWindow",
|
||||
titleBarTopPadding: Platform.isMacOS ? 20 : 0,
|
||||
userDataFolderWindows: await _getWebViewPath(),
|
||||
),
|
||||
);
|
||||
webview
|
||||
..registerJavaScriptMessageHandler("test", (name, body) {
|
||||
debugPrint('on javaScipt message: $name $body');
|
||||
})
|
||||
..setApplicationNameForUserAgent(" WebviewExample/1.0.0")
|
||||
..setPromptHandler((prompt, defaultText) {
|
||||
if (prompt == "test") {
|
||||
return "Hello World!";
|
||||
} else if (prompt == "init") {
|
||||
return "initial prompt";
|
||||
}
|
||||
return "";
|
||||
})
|
||||
..addScriptToExecuteOnDocumentCreated("""
|
||||
const mixinContext = {
|
||||
platform: 'Desktop',
|
||||
conversation_id: 'conversationId',
|
||||
immersive: false,
|
||||
app_version: '1.0.0',
|
||||
appearance: 'dark',
|
||||
}
|
||||
window.MixinContext = {
|
||||
getContext: function() {
|
||||
return JSON.stringify(mixinContext)
|
||||
}
|
||||
}
|
||||
""")
|
||||
..launch("http://localhost:3000/test.html");
|
||||
},
|
||||
icon: const Icon(Icons.bug_report),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
TextField(controller: _controller),
|
||||
const SizedBox(height: 16),
|
||||
TextButton(
|
||||
onPressed: _webviewAvailable != true ? null : _onTap,
|
||||
child: const Text('Open'),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await WebviewWindow.clearAll(
|
||||
userDataFolderWindows: await _getWebViewPath(),
|
||||
);
|
||||
debugPrint('clear complete');
|
||||
},
|
||||
child: const Text('Clear all'),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onTap() async {
|
||||
final webview = await WebviewWindow.create(
|
||||
configuration: CreateConfiguration(
|
||||
userDataFolderWindows: await _getWebViewPath(),
|
||||
titleBarTopPadding: Platform.isMacOS ? 20 : 0,
|
||||
),
|
||||
);
|
||||
webview
|
||||
..setBrightness(Brightness.dark)
|
||||
..setApplicationNameForUserAgent(" WebviewExample/1.0.0")
|
||||
..launch(_controller.text)
|
||||
..addOnUrlRequestCallback((url) {
|
||||
debugPrint('url: $url');
|
||||
final uri = Uri.parse(url);
|
||||
if (uri.path == '/login_success') {
|
||||
debugPrint('login success. token: ${uri.queryParameters['token']}');
|
||||
webview.close();
|
||||
}
|
||||
})
|
||||
..onClose.whenComplete(() {
|
||||
debugPrint("on close");
|
||||
});
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
for (final javaScript in _javaScriptToEval) {
|
||||
try {
|
||||
final ret = await webview.evaluateJavaScript(javaScript);
|
||||
debugPrint('evaluateJavaScript: $ret');
|
||||
} catch (e) {
|
||||
debugPrint('evaluateJavaScript error: $e \n $javaScript');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _javaScriptToEval = [
|
||||
"""
|
||||
function test() {
|
||||
return;
|
||||
}
|
||||
test();
|
||||
""",
|
||||
'eval({"name": "test", "user_agent": navigator.userAgent})',
|
||||
'1 + 1',
|
||||
'undefined',
|
||||
'1.0 + 1.0',
|
||||
'"test"',
|
||||
];
|
||||
|
||||
Future<String> _getWebViewPath() async {
|
||||
final document = await getApplicationDocumentsDirectory();
|
||||
return p.join(
|
||||
document.path,
|
||||
'desktop_webview_window',
|
||||
);
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import desktop_webview_window
|
||||
import path_provider_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=C:\flutter
|
||||
FLUTTER_APPLICATION_PATH=C:\DEV\flutter\mangayomi\packages\desktop_webview_window\example
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=1.0.0
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=.dart_tool/package_config.json
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=C:\flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=C:\DEV\flutter\mangayomi\packages\desktop_webview_window\example"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
platform :osx, '10.12'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_macos_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_macos_build_settings(target)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
PODS:
|
||||
- desktop_webview_window (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- path_provider_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
desktop_webview_window:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
path_provider_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
desktop_webview_window: d4365e71bcd4e1aa0c14cf0377aa24db0c16a7e2
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||
|
||||
PODFILE CHECKSUM: c7161fcf45d4fd9025dc0f48a76d6e64e52f8176
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
|
|
@ -1,635 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
|
||||
buildPhases = (
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Flutter Assemble";
|
||||
productName = FLX;
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
127EA8516D3B0432D8A2C324 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B451012A2EB682A5199F610 /* Pods_Runner.framework */; };
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
|
||||
remoteInfo = FLX;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Bundle Framework";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2DA652A6747DAACB460CA29B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* webview_window_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = webview_window_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
487BD0E504B87804F9B4770E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
60E196C41ABF89028378EB94 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
6B451012A2EB682A5199F610 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
127EA8516D3B0432D8A2C324 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0FC36F805FF9C8AF745F5250 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
60E196C41ABF89028378EB94 /* Pods-Runner.debug.xcconfig */,
|
||||
2DA652A6747DAACB460CA29B /* Pods-Runner.release.xcconfig */,
|
||||
487BD0E504B87804F9B4770E /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33BA886A226E78AF003329D5 /* Configs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
|
||||
);
|
||||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10E42044A3C60003C045 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33FAB671232836740065AC1E /* Runner */,
|
||||
33CEB47122A05771004F2AC0 /* Flutter */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
0FC36F805FF9C8AF745F5250 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10ED2044A3C60003C045 /* webview_window_example.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC11242044D66E0003C045 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */,
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */,
|
||||
33CC10F72044A3C60003C045 /* Info.plist */,
|
||||
);
|
||||
name = Resources;
|
||||
path = ..;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CEB47122A05771004F2AC0 /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
|
||||
);
|
||||
path = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33FAB671232836740065AC1E /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
||||
33E51914231749380026EE4D /* Release.entitlements */,
|
||||
33CC11242044D66E0003C045 /* Resources */,
|
||||
33BA886A226E78AF003329D5 /* Configs */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6B451012A2EB682A5199F610 /* Pods_Runner.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
33CC10EC2044A3C60003C045 /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
AFBF2BCAB0A65734F883B596 /* [CP] Check Pods Manifest.lock */,
|
||||
33CC10E92044A3C60003C045 /* Sources */,
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||
33CC10EB2044A3C60003C045 /* Resources */,
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
4C068521E38575C134D7A34F /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* webview_window_example.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
33CC10E52044A3C60003C045 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1430;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
33CC10EC2044A3C60003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
33CC111A2044C6BA0003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 33CC10E42044A3C60003C045;
|
||||
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
33CC10EC2044A3C60003C045 /* Runner */,
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
33CC10EB2044A3C60003C045 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
|
||||
};
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterInputs.xcfilelist,
|
||||
);
|
||||
inputPaths = (
|
||||
Flutter/ephemeral/tripwire,
|
||||
);
|
||||
outputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterOutputs.xcfilelist,
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||
};
|
||||
4C068521E38575C134D7A34F /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
AFBF2BCAB0A65734F883B596 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
33CC10E92044A3C60003C045 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
|
||||
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
33CC10F52044A3C60003C045 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
338D0CE9231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEA231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEB231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
33CC10F92044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FA2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC10FC2044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FD2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC111C2044C6BA0003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC111D2044C6BA0003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10F92044A3C60003C045 /* Debug */,
|
||||
33CC10FA2044A3C60003C045 /* Release */,
|
||||
338D0CE9231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10FC2044A3C60003C045 /* Debug */,
|
||||
33CC10FD2044A3C60003C045 /* Release */,
|
||||
338D0CEA231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC111C2044C6BA0003C045 /* Debug */,
|
||||
33CC111D2044C6BA0003C045 /* Release */,
|
||||
338D0CEB231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "webview_window_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "webview_window_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "webview_window_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "webview_window_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -1,340 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19162" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19162"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="webview_window_example" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
|
||||
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="APP_NAME" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About APP_NAME" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||
<connections>
|
||||
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||
<connections>
|
||||
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||
<menuItem title="Find" id="4EN-yA-p0u">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="H8h-7b-M4v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="142" y="-258"/>
|
||||
</menu>
|
||||
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="webview_window_example" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</view>
|
||||
<point key="canvasLocation" x="68" y="227"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Application-level settings for the Runner target.
|
||||
//
|
||||
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
|
||||
// future. If not, the values below would default to using the project name when this becomes a
|
||||
// 'flutter create' template.
|
||||
|
||||
// The application's name. By default this is also the title of the Flutter window.
|
||||
PRODUCT_NAME = webview_window_example
|
||||
|
||||
// The application's bundle identifier
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.webviewWindowExample
|
||||
|
||||
// The copyright displayed in application information
|
||||
PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved.
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include "../../Flutter/Flutter-Debug.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include "../../Flutter/Flutter-Release.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES
|
||||
CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_PRAGMA_PACK = YES
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES
|
||||
CLANG_WARN_COMMA = YES
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES
|
||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
|
||||
GCC_WARN_SHADOW = YES
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoadsInWebContent</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController.init()
|
||||
let windowFrame = self.frame
|
||||
self.contentViewController = flutterViewController
|
||||
self.setFrame(windowFrame, display: true)
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
desktop_webview_window:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.2.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.4"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.6"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
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: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sdks:
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
name: webview_window_example
|
||||
description: Demonstrates how to use the webview_window plugin.
|
||||
|
||||
# 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
|
||||
|
||||
environment:
|
||||
sdk: ">=2.14.0 <3.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:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
desktop_webview_window:
|
||||
# When depending on this package from a real application you should use:
|
||||
# webview_window: ^x.y.z
|
||||
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ../
|
||||
path_provider: ^2.0.7
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# 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: ^1.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.
|
||||
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
|
||||
|
||||
# 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/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# 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/custom-fonts/#from-packages
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
void main() {}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Redirect if login</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Auto redirect after to 2 seconds.
|
||||
|
||||
<script type="application/javascript">
|
||||
setTimeout(function () {
|
||||
window.location.href = "login_success?username=test&token=xxxxx";
|
||||
}, 2000);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Webview Window</title>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function readCookie() {
|
||||
document.getElementById("cookie_text").innerText = document.cookie;
|
||||
}
|
||||
|
||||
function postSimpleMessage() {
|
||||
if (window.webkit) {
|
||||
window.webkit.messageHandlers.test.postMessage("Hello");
|
||||
}
|
||||
}
|
||||
|
||||
function postMapMessage() {
|
||||
if (window.webkit) {
|
||||
window.webkit.messageHandlers.test.postMessage({"key1": 0, "key2": "value"});
|
||||
}
|
||||
}
|
||||
|
||||
function promptMessage() {
|
||||
let result = prompt("test", "hello");
|
||||
document.getElementById("prompt_result").innerText = result;
|
||||
}
|
||||
|
||||
function updateCookie() {
|
||||
document.cookie = "username=test; date=2021"
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Hello World!
|
||||
|
||||
<p id="context">
|
||||
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<button type="button" onclick="postSimpleMessage()">PostSimpleMessage</button>
|
||||
<button type="button" onclick="postMapMessage()">PostMapMessage</button>
|
||||
|
||||
<br/>
|
||||
<button type="button" onclick="promptMessage()">PromptMessage</button>
|
||||
<p id="prompt_result">
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<button type="button" onclick="updateCookie()">updateCookie</button>
|
||||
<p id="cookie_text"></p>
|
||||
|
||||
<br>
|
||||
<p id="user_agent_text"></p>
|
||||
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
|
||||
function getMixinContext() {
|
||||
let ctx = {};
|
||||
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.MixinContext) {
|
||||
ctx = JSON.parse(prompt('MixinContext.getContext()'))
|
||||
ctx.platform = ctx.platform || 'iOS'
|
||||
} else if (window.MixinContext && (typeof window.MixinContext.getContext === 'function')) {
|
||||
ctx = JSON.parse(window.MixinContext.getContext())
|
||||
ctx.platform = ctx.platform || 'Android'
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
let context = getMixinContext();
|
||||
|
||||
var e = window.document.getElementById("context");
|
||||
if (context === undefined) {
|
||||
e.innerText = "context not defined";
|
||||
} else {
|
||||
e.innerText = JSON.stringify(context);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// document.getElementById("prompt_result").innerText = prompt("init");
|
||||
readCookie()
|
||||
|
||||
document.getElementById("user_agent_text").innerText = navigator.userAgent;
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
// You have generated a new plugin project without
|
||||
// specifying the `--platforms` flag. A plugin project supports no platforms is generated.
|
||||
// To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
|
||||
// directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'src/create_configuration.dart';
|
||||
import 'src/message_channel.dart';
|
||||
import 'src/webview.dart';
|
||||
import 'src/webview_impl.dart';
|
||||
|
||||
export 'src/create_configuration.dart';
|
||||
export 'src/title_bar.dart';
|
||||
export 'src/webview.dart';
|
||||
|
||||
final List<WebviewImpl> _webviews = [];
|
||||
|
||||
class WebviewWindow {
|
||||
static const MethodChannel _channel = MethodChannel('webview_window');
|
||||
|
||||
static const _otherIsolateMessageHandler = ClientMessageChannel();
|
||||
|
||||
static bool _inited = false;
|
||||
|
||||
static void _init() {
|
||||
if (_inited) {
|
||||
return;
|
||||
}
|
||||
_inited = true;
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethodCall(call);
|
||||
} catch (e, s) {
|
||||
debugPrint("method: ${call.method} args: ${call.arguments}");
|
||||
debugPrint('handleMethodCall error: $e $s');
|
||||
}
|
||||
});
|
||||
_otherIsolateMessageHandler.setMessageHandler((call) async {
|
||||
try {
|
||||
return await _handleOtherIsolateMethodCall(call);
|
||||
} catch (e, s) {
|
||||
debugPrint('_handleOtherIsolateMethodCall error: $e $s');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Check if WebView runtime is available on the current devices.
|
||||
static Future<bool> isWebviewAvailable() async {
|
||||
if (Platform.isWindows) {
|
||||
final ret = await _channel.invokeMethod<bool>('isWebviewAvailable');
|
||||
return ret == true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<Webview> create({
|
||||
CreateConfiguration? configuration,
|
||||
}) async {
|
||||
configuration ??= CreateConfiguration.platform();
|
||||
_init();
|
||||
final viewId = await _channel.invokeMethod(
|
||||
"create",
|
||||
configuration.toMap(),
|
||||
) as int;
|
||||
final webview = WebviewImpl(viewId, _channel);
|
||||
_webviews.add(webview);
|
||||
return webview;
|
||||
}
|
||||
|
||||
static Future<dynamic> _handleOtherIsolateMethodCall(MethodCall call) async {
|
||||
final webViewId = call.arguments['webViewId'] as int;
|
||||
final webView = _webviews
|
||||
.cast<WebviewImpl?>()
|
||||
.firstWhere((w) => w?.viewId == webViewId, orElse: () => null);
|
||||
if (webView == null) {
|
||||
return;
|
||||
}
|
||||
switch (call.method) {
|
||||
case 'onBackPressed':
|
||||
await webView.back();
|
||||
break;
|
||||
case 'onForwardPressed':
|
||||
await webView.forward();
|
||||
break;
|
||||
case 'onRefreshPressed':
|
||||
await webView.reload();
|
||||
break;
|
||||
case 'onStopPressed':
|
||||
await webView.stop();
|
||||
break;
|
||||
case 'onClosePressed':
|
||||
webView.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<dynamic> _handleMethodCall(MethodCall call) async {
|
||||
final args = call.arguments as Map;
|
||||
final viewId = args['id'] as int;
|
||||
final webview = _webviews
|
||||
.cast<WebviewImpl?>()
|
||||
.firstWhere((e) => e?.viewId == viewId, orElse: () => null);
|
||||
assert(webview != null);
|
||||
if (webview == null) {
|
||||
return;
|
||||
}
|
||||
switch (call.method) {
|
||||
case "onWindowClose":
|
||||
_webviews.remove(webview);
|
||||
webview.onClosed();
|
||||
break;
|
||||
case "onJavaScriptMessage":
|
||||
webview.onJavaScriptMessage(args['name'], args['body']);
|
||||
break;
|
||||
case "runJavaScriptTextInputPanelWithPrompt":
|
||||
return webview.onRunJavaScriptTextInputPanelWithPrompt(
|
||||
args['prompt'],
|
||||
args['defaultText'],
|
||||
);
|
||||
case "onHistoryChanged":
|
||||
webview.onHistoryChanged(args['canGoBack'], args['canGoForward']);
|
||||
await _otherIsolateMessageHandler.invokeMethod('onHistoryChanged', {
|
||||
'webViewId': viewId,
|
||||
'canGoBack': args['canGoBack'] as bool,
|
||||
'canGoForward': args['canGoForward'] as bool,
|
||||
});
|
||||
break;
|
||||
case "onNavigationStarted":
|
||||
webview.onNavigationStarted();
|
||||
await _otherIsolateMessageHandler.invokeMethod('onNavigationStarted', {
|
||||
'webViewId': viewId,
|
||||
});
|
||||
break;
|
||||
case "onUrlRequested":
|
||||
final url = args['url'] as String;
|
||||
webview.notifyUrlChanged(url);
|
||||
await _otherIsolateMessageHandler.invokeMethod('onUrlRequested', {
|
||||
'webViewId': viewId,
|
||||
'url': url,
|
||||
});
|
||||
break;
|
||||
case "onWebMessageReceived":
|
||||
final message = args['message'] as String;
|
||||
webview.notifyWebMessageReceived(message);
|
||||
await _otherIsolateMessageHandler.invokeMethod('onWebMessageReceived', {
|
||||
'webViewId': viewId,
|
||||
'message': message,
|
||||
});
|
||||
break;
|
||||
case "onNavigationCompleted":
|
||||
webview.onNavigationCompleted();
|
||||
await _otherIsolateMessageHandler
|
||||
.invokeMethod('onNavigationCompleted', {
|
||||
'webViewId': viewId,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all cookies and storage.
|
||||
static Future<void> clearAll({
|
||||
String userDataFolderWindows = 'webview_window_WebView2',
|
||||
}) async {
|
||||
await _channel.invokeMethod('clearAll');
|
||||
|
||||
// FIXME(boyan01) Move the logic to windows platform if WebView2 provider a way to clean caches.
|
||||
// https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/user-data-folder#create-user-data-folders
|
||||
if (Platform.isWindows) {
|
||||
final Directory webview2Dir;
|
||||
if (p.isAbsolute(userDataFolderWindows)) {
|
||||
webview2Dir = Directory(userDataFolderWindows);
|
||||
} else {
|
||||
webview2Dir = Directory(p.join(
|
||||
p.dirname(Platform.resolvedExecutable), userDataFolderWindows));
|
||||
}
|
||||
|
||||
if (await (webview2Dir.exists())) {
|
||||
for (var i = 0; i <= 4; i++) {
|
||||
try {
|
||||
await webview2Dir.delete(recursive: true);
|
||||
break;
|
||||
} catch (e) {
|
||||
debugPrint("delete cache failed. retring.... $e");
|
||||
}
|
||||
// wait to ensure all web window has been closed and file handle has been release.
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
class CreateConfiguration {
|
||||
final int windowWidth;
|
||||
final int windowHeight;
|
||||
|
||||
/// Position of the top left point of the webview window
|
||||
final int windowPosX;
|
||||
final int windowPosY;
|
||||
|
||||
/// the title of window
|
||||
final String title;
|
||||
|
||||
final int titleBarHeight;
|
||||
|
||||
final int titleBarTopPadding;
|
||||
|
||||
final String userDataFolderWindows;
|
||||
|
||||
final bool useWindowPositionAndSize;
|
||||
final bool openMaximized;
|
||||
|
||||
const CreateConfiguration({
|
||||
this.windowWidth = 1280,
|
||||
this.windowHeight = 720,
|
||||
this.windowPosX = 0,
|
||||
this.windowPosY = 0,
|
||||
this.title = "",
|
||||
this.titleBarHeight = 40,
|
||||
this.titleBarTopPadding = 0,
|
||||
this.userDataFolderWindows = 'webview_window_WebView2',
|
||||
this.useWindowPositionAndSize = false,
|
||||
this.openMaximized = false,
|
||||
});
|
||||
|
||||
factory CreateConfiguration.platform() {
|
||||
return CreateConfiguration(
|
||||
titleBarTopPadding: Platform.isMacOS ? 24 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map toMap() => {
|
||||
"windowWidth": windowWidth,
|
||||
"windowHeight": windowHeight,
|
||||
"windowPosX": windowPosX,
|
||||
"windowPosY": windowPosY,
|
||||
"title": title,
|
||||
"titleBarHeight": titleBarHeight,
|
||||
"titleBarTopPadding": titleBarTopPadding,
|
||||
"userDataFolderWindows": userDataFolderWindows,
|
||||
"useWindowPositionAndSize": useWindowPositionAndSize,
|
||||
"openMaximized": openMaximized,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import 'package:flutter/services.dart';
|
||||
|
||||
typedef MessageHandler = Future<dynamic> Function(MethodCall call);
|
||||
|
||||
class ClientMessageChannel {
|
||||
const ClientMessageChannel();
|
||||
|
||||
static const _channel = MethodChannel('webview_message/client_channel');
|
||||
|
||||
Future<dynamic> invokeMethod(String method, [dynamic arguments]) {
|
||||
return _channel.invokeMethod(method, arguments);
|
||||
}
|
||||
|
||||
void setMessageHandler(MessageHandler? handler) {
|
||||
_channel.setMethodCallHandler(handler);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'message_channel.dart';
|
||||
|
||||
const _channel = ClientMessageChannel();
|
||||
|
||||
/// runs the title bar
|
||||
/// title bar is a widget that displays the title of the webview window
|
||||
/// return true if the args is matchs the title bar
|
||||
///
|
||||
/// [builder] custom TitleBar widget builder.
|
||||
/// can use [TitleBarWebViewController] to controller the WebView
|
||||
/// use [TitleBarWebViewState] to triger the title bar status.
|
||||
///
|
||||
bool runWebViewTitleBarWidget(
|
||||
List<String> args, {
|
||||
WidgetBuilder? builder,
|
||||
Color? backgroundColor,
|
||||
void Function(Object error, StackTrace stack)? onError,
|
||||
}) {
|
||||
if (args.isEmpty || args[0] != 'web_view_title_bar') {
|
||||
return false;
|
||||
}
|
||||
final webViewId = int.tryParse(args[1]);
|
||||
if (webViewId == null) {
|
||||
return false;
|
||||
}
|
||||
final titleBarTopPadding = int.tryParse(args.length > 2 ? args[2] : '0') ?? 0;
|
||||
runZonedGuarded(
|
||||
() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(_TitleBarApp(
|
||||
webViewId: webViewId,
|
||||
titleBarTopPadding: titleBarTopPadding,
|
||||
backgroundColor: backgroundColor,
|
||||
builder: builder ?? _defaultTitleBar,
|
||||
));
|
||||
},
|
||||
onError ??
|
||||
(e, s) {
|
||||
debugPrint('WebViewTitleBar: unhandled expections: $e, $s');
|
||||
},
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mixin TitleBarWebViewController {
|
||||
static TitleBarWebViewController of(BuildContext context) {
|
||||
final state = context.findAncestorStateOfType<_TitleBarAppState>();
|
||||
assert(state != null,
|
||||
'only can find TitleBarWebViewController in widget which run from runWebViewTitleBarWidget');
|
||||
return state!;
|
||||
}
|
||||
|
||||
int get _webViewId;
|
||||
|
||||
/// navigate back
|
||||
void back() {
|
||||
_channel.invokeMethod('onBackPressed', {
|
||||
'webViewId': _webViewId,
|
||||
});
|
||||
}
|
||||
|
||||
/// navigate forward
|
||||
void forward() {
|
||||
_channel.invokeMethod('onForwardPressed', {
|
||||
'webViewId': _webViewId,
|
||||
});
|
||||
}
|
||||
|
||||
/// reload the webview
|
||||
void reload() {
|
||||
_channel.invokeMethod('onRefreshPressed', {
|
||||
'webViewId': _webViewId,
|
||||
});
|
||||
}
|
||||
|
||||
/// stop loading the webview
|
||||
void stop() {
|
||||
_channel.invokeMethod('onStopPressed', {
|
||||
'webViewId': _webViewId,
|
||||
});
|
||||
}
|
||||
|
||||
/// close the webview
|
||||
void close() {
|
||||
_channel.invokeMethod('onClosePressed', {
|
||||
'webViewId': _webViewId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TitleBarWebViewState extends InheritedWidget {
|
||||
const TitleBarWebViewState({
|
||||
Key? key,
|
||||
required Widget child,
|
||||
required this.isLoading,
|
||||
required this.canGoBack,
|
||||
required this.canGoForward,
|
||||
required this.url,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
final bool isLoading;
|
||||
final bool canGoBack;
|
||||
final bool canGoForward;
|
||||
final String? url;
|
||||
|
||||
static TitleBarWebViewState of(BuildContext context) {
|
||||
final TitleBarWebViewState? result =
|
||||
context.dependOnInheritedWidgetOfExactType<TitleBarWebViewState>();
|
||||
assert(result != null, 'No WebViewState found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(TitleBarWebViewState oldWidget) {
|
||||
return isLoading != oldWidget.isLoading ||
|
||||
canGoBack != oldWidget.canGoBack ||
|
||||
canGoForward != oldWidget.canGoForward;
|
||||
}
|
||||
}
|
||||
|
||||
class _TitleBarApp extends StatefulWidget {
|
||||
const _TitleBarApp({
|
||||
Key? key,
|
||||
required this.webViewId,
|
||||
required this.titleBarTopPadding,
|
||||
required this.builder,
|
||||
this.backgroundColor,
|
||||
}) : super(key: key);
|
||||
|
||||
final int webViewId;
|
||||
|
||||
final int titleBarTopPadding;
|
||||
|
||||
final WidgetBuilder builder;
|
||||
|
||||
final Color? backgroundColor;
|
||||
|
||||
@override
|
||||
State<_TitleBarApp> createState() => _TitleBarAppState();
|
||||
}
|
||||
|
||||
class _TitleBarAppState extends State<_TitleBarApp>
|
||||
with TitleBarWebViewController {
|
||||
bool _canGoBack = false;
|
||||
bool _canGoForward = false;
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
String? _url;
|
||||
|
||||
@override
|
||||
int get _webViewId => widget.webViewId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_channel.setMessageHandler((call) async {
|
||||
final args = call.arguments as Map;
|
||||
final webViewId = args['webViewId'] as int;
|
||||
if (webViewId != widget.webViewId) {
|
||||
return;
|
||||
}
|
||||
switch (call.method) {
|
||||
case "onHistoryChanged":
|
||||
setState(() {
|
||||
_canGoBack = args['canGoBack'] as bool;
|
||||
_canGoForward = args['canGoForward'] as bool;
|
||||
});
|
||||
break;
|
||||
case "onNavigationStarted":
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
break;
|
||||
case "onNavigationCompleted":
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
break;
|
||||
case "onUrlRequested":
|
||||
setState(() {
|
||||
_url = args['url'] as String;
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Material(
|
||||
color:
|
||||
widget.backgroundColor ?? Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: widget.titleBarTopPadding.toDouble()),
|
||||
child: TitleBarWebViewState(
|
||||
isLoading: _isLoading,
|
||||
canGoBack: _canGoBack,
|
||||
canGoForward: _canGoForward,
|
||||
url: _url,
|
||||
child: Builder(builder: widget.builder),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _defaultTitleBar(BuildContext context) {
|
||||
final state = TitleBarWebViewState.of(context);
|
||||
final controller = TitleBarWebViewController.of(context);
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 16,
|
||||
iconSize: 16,
|
||||
onPressed: !state.canGoBack ? null : controller.back,
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 16,
|
||||
iconSize: 16,
|
||||
onPressed: !state.canGoForward ? null : controller.forward,
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
),
|
||||
if (state.isLoading)
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 16,
|
||||
iconSize: 16,
|
||||
onPressed: controller.stop,
|
||||
icon: const Icon(Icons.close),
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 16,
|
||||
iconSize: 16,
|
||||
onPressed: controller.reload,
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
const Spacer()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Handle custom message from JavaScript in your app.
|
||||
typedef JavaScriptMessageHandler = void Function(String name, dynamic body);
|
||||
|
||||
typedef PromptHandler = String Function(String prompt, String defaultText);
|
||||
|
||||
typedef OnHistoryChangedCallback = void Function(
|
||||
bool canGoBack, bool canGoForward);
|
||||
|
||||
/// Callback when WebView start to load a URL.
|
||||
/// [url] is the URL string.
|
||||
typedef OnUrlRequestCallback = void Function(String url);
|
||||
|
||||
/// Callback when WebView receives a web message
|
||||
/// [message] constains the webmessage
|
||||
typedef OnWebMessageReceivedCallback = void Function(String message);
|
||||
|
||||
abstract class Webview {
|
||||
Future<void> get onClose;
|
||||
|
||||
/// true if the webview is currently loading a page.
|
||||
ValueListenable<bool> get isNavigating;
|
||||
|
||||
/// Install a message handler that you can call from your Javascript code.
|
||||
///
|
||||
/// available: macOS (10.10+)
|
||||
void registerJavaScriptMessageHandler(
|
||||
String name, JavaScriptMessageHandler handler);
|
||||
|
||||
/// available: macOS
|
||||
void unregisterJavaScriptMessageHandler(String name);
|
||||
|
||||
/// available: macOS
|
||||
void setPromptHandler(PromptHandler? handler);
|
||||
|
||||
/// Navigates to the given URL.
|
||||
void launch(String url);
|
||||
|
||||
/// change webview theme.
|
||||
///
|
||||
/// available only: macOS (Brightness.dark only 10.14+)
|
||||
void setBrightness(Brightness? brightness);
|
||||
|
||||
void addScriptToExecuteOnDocumentCreated(String javaScript);
|
||||
|
||||
/// Append a string to the webview's user-agent.
|
||||
Future<void> setApplicationNameForUserAgent(String applicationName);
|
||||
|
||||
/// Navigate to the previous page in the history.
|
||||
Future<void> back();
|
||||
|
||||
/// Navigate to the next page in the history.
|
||||
Future<void> forward();
|
||||
|
||||
/// Show or hide webview window
|
||||
Future<void> setWebviewWindowVisibility(bool visible);
|
||||
|
||||
/// Reload the current page.
|
||||
Future<void> reload();
|
||||
|
||||
/// Stop all navigations and pending resource fetches.
|
||||
Future<void> stop();
|
||||
|
||||
/// Opens the Browser DevTools in a separate window
|
||||
Future<void> openDevToolsWindow();
|
||||
|
||||
/// Register a callback that will be invoked when the webview history changes.
|
||||
void setOnHistoryChangedCallback(OnHistoryChangedCallback? callback);
|
||||
|
||||
void addOnUrlRequestCallback(OnUrlRequestCallback callback);
|
||||
|
||||
void removeOnUrlRequestCallback(OnUrlRequestCallback callback);
|
||||
|
||||
void addOnWebMessageReceivedCallback(OnWebMessageReceivedCallback callback);
|
||||
|
||||
void removeOnWebMessageReceivedCallback(
|
||||
OnWebMessageReceivedCallback callback);
|
||||
|
||||
/// Close the web view window.
|
||||
void close();
|
||||
|
||||
/// evaluate JavaScript in the web view.
|
||||
Future<String?> evaluateJavaScript(String javaScript);
|
||||
|
||||
/// post a web message as String to the top level document in this WebView
|
||||
Future<void> postWebMessageAsString(String webMessage);
|
||||
|
||||
/// post a web message as JSON to the top level document in this WebView
|
||||
Future<void> postWebMessageAsJson(String webMessage);
|
||||
|
||||
Future<List<Map<String, dynamic>>> getCookies(String url);
|
||||
}
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'webview.dart';
|
||||
|
||||
class WebviewImpl extends Webview {
|
||||
final int viewId;
|
||||
|
||||
final MethodChannel channel;
|
||||
|
||||
final Map<String, JavaScriptMessageHandler> _javaScriptMessageHandlers = {};
|
||||
|
||||
bool _closed = false;
|
||||
|
||||
PromptHandler? _promptHandler;
|
||||
|
||||
final _closeCompleter = Completer<void>();
|
||||
|
||||
OnHistoryChangedCallback? _onHistoryChanged;
|
||||
|
||||
final ValueNotifier<bool> _isNaivgating = ValueNotifier<bool>(false);
|
||||
|
||||
final Set<OnUrlRequestCallback> _onUrlRequestCallbacks = {};
|
||||
|
||||
final Set<OnWebMessageReceivedCallback> _onWebMessageReceivedCallbacks = {};
|
||||
|
||||
WebviewImpl(this.viewId, this.channel);
|
||||
|
||||
@override
|
||||
Future<void> get onClose => _closeCompleter.future;
|
||||
|
||||
void onClosed() {
|
||||
_closed = true;
|
||||
_closeCompleter.complete();
|
||||
}
|
||||
|
||||
void onJavaScriptMessage(String name, dynamic body) {
|
||||
assert(!_closed);
|
||||
final handler = _javaScriptMessageHandlers[name];
|
||||
assert(handler != null, "handler $name is not registed.");
|
||||
handler?.call(name, body);
|
||||
}
|
||||
|
||||
String onRunJavaScriptTextInputPanelWithPrompt(
|
||||
String prompt, String defaultText) {
|
||||
assert(!_closed);
|
||||
return _promptHandler?.call(prompt, defaultText) ?? defaultText;
|
||||
}
|
||||
|
||||
void onHistoryChanged(bool canGoBack, bool canGoForward) {
|
||||
assert(!_closed);
|
||||
_onHistoryChanged?.call(canGoBack, canGoForward);
|
||||
}
|
||||
|
||||
void onNavigationStarted() {
|
||||
_isNaivgating.value = true;
|
||||
}
|
||||
|
||||
void notifyUrlChanged(String url) {
|
||||
for (final callback in _onUrlRequestCallbacks) {
|
||||
callback(url);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyWebMessageReceived(String message) {
|
||||
for (final callback in _onWebMessageReceivedCallbacks) {
|
||||
callback(message);
|
||||
}
|
||||
}
|
||||
|
||||
void onNavigationCompleted() {
|
||||
_isNaivgating.value = false;
|
||||
}
|
||||
|
||||
@override
|
||||
ValueListenable<bool> get isNavigating => _isNaivgating;
|
||||
|
||||
@override
|
||||
void registerJavaScriptMessageHandler(
|
||||
String name, JavaScriptMessageHandler handler) {
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
assert(!_closed);
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
assert(name.isNotEmpty);
|
||||
assert(!_javaScriptMessageHandlers.containsKey(name));
|
||||
_javaScriptMessageHandlers[name] = handler;
|
||||
channel.invokeMethod("registerJavaScripInterface", {
|
||||
"viewId": viewId,
|
||||
"name": name,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void unregisterJavaScriptMessageHandler(String name) {
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
channel.invokeMethod("unregisterJavaScripInterface", {
|
||||
"viewId": viewId,
|
||||
"name": name,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void setPromptHandler(PromptHandler? handler) {
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
_promptHandler = handler;
|
||||
}
|
||||
|
||||
@override
|
||||
void launch(String url) async {
|
||||
await channel.invokeMethod("launch", {
|
||||
"url": url,
|
||||
"viewId": viewId,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void setBrightness(Brightness? brightness) {
|
||||
/// -1 : system default
|
||||
/// 0 : dark
|
||||
/// 1 : light
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
channel.invokeMethod("setBrightness", {
|
||||
"viewId": viewId,
|
||||
"brightness": brightness?.index ?? -1,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void addScriptToExecuteOnDocumentCreated(String javaScript) {
|
||||
if (!(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||
return;
|
||||
}
|
||||
assert(javaScript.trim().isNotEmpty);
|
||||
channel.invokeMethod("addScriptToExecuteOnDocumentCreated", {
|
||||
"viewId": viewId,
|
||||
"javaScript": javaScript,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setApplicationNameForUserAgent(String applicationName) async {
|
||||
if (!(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||
return;
|
||||
}
|
||||
await channel.invokeMethod("setApplicationNameForUserAgent", {
|
||||
"viewId": viewId,
|
||||
"applicationName": applicationName,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> forward() {
|
||||
return channel.invokeMethod("forward", {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setWebviewWindowVisibility(bool visible) {
|
||||
return channel.invokeMethod("setWebviewWindowVisibility", {
|
||||
"viewId": viewId,
|
||||
"visible": visible,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> back() {
|
||||
return channel.invokeMethod("back", {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> reload() {
|
||||
return channel.invokeMethod("reload", {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() {
|
||||
return channel.invokeMethod("stop", {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> openDevToolsWindow() {
|
||||
return channel.invokeMethod('openDevToolsWindow', {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
void setOnHistoryChangedCallback(OnHistoryChangedCallback? callback) {
|
||||
_onHistoryChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
void addOnUrlRequestCallback(OnUrlRequestCallback callback) {
|
||||
_onUrlRequestCallbacks.add(callback);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeOnUrlRequestCallback(OnUrlRequestCallback callback) {
|
||||
_onUrlRequestCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
@override
|
||||
void addOnWebMessageReceivedCallback(OnWebMessageReceivedCallback callback) {
|
||||
_onWebMessageReceivedCallbacks.add(callback);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeOnWebMessageReceivedCallback(
|
||||
OnWebMessageReceivedCallback callback) {
|
||||
_onWebMessageReceivedCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
@override
|
||||
void close() {
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
channel.invokeMethod("close", {"viewId": viewId});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> evaluateJavaScript(String javaScript) async {
|
||||
final dynamic result = await channel.invokeMethod("evaluateJavaScript", {
|
||||
"viewId": viewId,
|
||||
"javaScriptString": javaScript,
|
||||
});
|
||||
if (result is String || result == null) {
|
||||
return result;
|
||||
}
|
||||
return json.encode(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> postWebMessageAsString(String webMessage) async {
|
||||
return channel.invokeMethod("postWebMessageAsString", {
|
||||
"viewId": viewId,
|
||||
"webMessage": webMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> postWebMessageAsJson(String webMessage) async {
|
||||
return channel.invokeMethod("postWebMessageAsJson", {
|
||||
"viewId": viewId,
|
||||
"webMessage": webMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Map<String, dynamic>>> getCookies(String url) async {
|
||||
final cookieListMap =
|
||||
await channel.invokeMethod<List>('getCookies', {'url': url}) ?? [];
|
||||
List<Map<String, dynamic>> cookies = [];
|
||||
for (var cookieMap in cookieListMap) {
|
||||
cookies.add({
|
||||
if (cookieMap["name"] != null) "name": cookieMap["name"],
|
||||
if (cookieMap["value"] != null) "value": cookieMap["value"],
|
||||
if (cookieMap["expiresDate"] != null)
|
||||
"expiresDate": cookieMap["expiresDate"],
|
||||
if (cookieMap["isSessionOnly"] != null)
|
||||
"isSessionOnly": cookieMap["isSessionOnly"],
|
||||
if (cookieMap["domain"] != null) "domain": cookieMap["domain"],
|
||||
if (cookieMap["isSecure"] != null) "isSecure": cookieMap["isSecure"],
|
||||
if (cookieMap["isHttpOnly"] != null)
|
||||
"isHttpOnly": cookieMap["isHttpOnly"],
|
||||
if (cookieMap["path"] != null) "path": cookieMap["path"]
|
||||
});
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,328 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import WebKit
|
||||
|
||||
private var viewId: Int64 = 0
|
||||
|
||||
public class DesktopWebviewWindowPlugin: NSObject, FlutterPlugin {
|
||||
private let methodChannel: FlutterMethodChannel
|
||||
|
||||
private var webviews: [Int64: WebviewWindowController] = [:]
|
||||
|
||||
public init(methodChannel: FlutterMethodChannel) {
|
||||
self.methodChannel = methodChannel
|
||||
super.init()
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "webview_window", binaryMessenger: registrar.messenger)
|
||||
let instance = DesktopWebviewWindowPlugin(methodChannel: channel)
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
ClientMessageChannelPlugin.register(with: registrar)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "create":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
let width = argument["windowWidth"] as? Int ?? 1280
|
||||
let height = argument["windowHeight"] as? Int ?? 720
|
||||
let title = argument["title"] as? String ?? ""
|
||||
let titleBarHeight = argument["titleBarHeight"] as? Int ?? 50
|
||||
let titleBarTopPadding = argument["titleBarTopPadding"] as? Int ?? 0
|
||||
|
||||
let controller = WebviewWindowController(
|
||||
viewId: viewId, methodChannel: methodChannel,
|
||||
width: width, height: height, title: title,
|
||||
titleBarHeight: titleBarHeight, titleBarTopPadding: titleBarTopPadding
|
||||
)
|
||||
controller.webviewPlugin = self
|
||||
webviews[viewId] = controller
|
||||
controller.showWindow(nil)
|
||||
result(viewId)
|
||||
viewId += 1
|
||||
break
|
||||
case "getCookies":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
guard let url = argument["url"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param url not found", details: nil))
|
||||
return
|
||||
}
|
||||
var cookieList: [[String: Any?]] = []
|
||||
if let urlHost = URL(string: url)?.host {
|
||||
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { (cookies) in
|
||||
for cookie in cookies {
|
||||
if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) {
|
||||
var sameSite: String? = nil
|
||||
if #available(macOS 10.15, *) {
|
||||
if let sameSiteValue = cookie.sameSitePolicy?.rawValue {
|
||||
sameSite = sameSiteValue.prefix(1).capitalized + sameSiteValue.dropFirst()
|
||||
}
|
||||
}
|
||||
var expiresDateTimestamp: Int64 = -1
|
||||
if let expiresDate = cookie.expiresDate?.timeIntervalSince1970 {
|
||||
// convert to milliseconds
|
||||
expiresDateTimestamp = Int64(expiresDate * 1000)
|
||||
}
|
||||
cookieList.append([
|
||||
"name": cookie.name,
|
||||
"value": cookie.value,
|
||||
"expiresDate": expiresDateTimestamp != -1 ? expiresDateTimestamp : nil,
|
||||
"isSessionOnly": cookie.isSessionOnly,
|
||||
"domain": cookie.domain,
|
||||
"sameSite": sameSite,
|
||||
"isSecure": cookie.isSecure,
|
||||
"isHttpOnly": cookie.isHTTPOnly,
|
||||
"path": cookie.path,
|
||||
])
|
||||
}
|
||||
}
|
||||
result(cookieList)
|
||||
}
|
||||
return
|
||||
}
|
||||
result(cookieList)
|
||||
break
|
||||
case "launch":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
guard let url = argument["url"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param url not found", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
guard let parsedUrl = URL(string: url) else {
|
||||
result(FlutterError(code: "0", message: "failed to parse \(url)", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.load(url: parsedUrl)
|
||||
result(nil)
|
||||
break
|
||||
case "registerJavaScripInterface":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let name = argument["name"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param name not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.addJavascriptInterface(name: name)
|
||||
result(nil)
|
||||
break
|
||||
case "unregisterJavaScripInterface":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let name = argument["name"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param name not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.addJavascriptInterface(name: name)
|
||||
result(nil)
|
||||
break
|
||||
case "clearAll":
|
||||
WKWebsiteDataStore.default().removeData(
|
||||
ofTypes: [WKWebsiteDataTypeCookies, WKWebsiteDataTypeLocalStorage],
|
||||
modifiedSince: .distantPast,
|
||||
completionHandler: {
|
||||
result(nil)
|
||||
})
|
||||
break
|
||||
case "setBrightness":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
let brightness = argument["brightness"] as? Int ?? -1
|
||||
wc.setAppearance(brightness: brightness)
|
||||
result(nil)
|
||||
break
|
||||
case "addScriptToExecuteOnDocumentCreated":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
guard let javaScript = argument["javaScript"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param javaScript not found", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.addScriptToExecuteOnDocumentCreated(javaScript: javaScript)
|
||||
result(nil)
|
||||
break
|
||||
case "setApplicationNameForUserAgent":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
guard let applicationName = argument["applicationName"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param applicationName not found", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.setApplicationNameForUserAgent(applicationName: applicationName)
|
||||
result(nil)
|
||||
break
|
||||
case "back":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.goBack()
|
||||
break
|
||||
case "forward":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.goForward()
|
||||
break
|
||||
case "reload":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.reload()
|
||||
break
|
||||
case "stop":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.stopLoading()
|
||||
break
|
||||
case "close":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
wc.close()
|
||||
break
|
||||
case "evaluateJavaScript":
|
||||
guard let argument = call.arguments as? [String: Any?] else {
|
||||
result(FlutterError(code: "0", message: "arg is not map", details: nil))
|
||||
return
|
||||
}
|
||||
guard let viewId = argument["viewId"] as? Int64 else {
|
||||
result(FlutterError(code: "0", message: "param viewId not found", details: nil))
|
||||
return
|
||||
}
|
||||
guard let wc = webviews[viewId] else {
|
||||
result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil))
|
||||
return
|
||||
}
|
||||
guard let js = argument["javaScriptString"] as? String else {
|
||||
result(FlutterError(code: "0", message: "param javaScriptString not found", details: nil))
|
||||
return
|
||||
}
|
||||
wc.webViewController.evaluateJavaScript(javaScriptString: js, completer: result)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
func onWebviewWindowClose(viewId: Int64, wc: WebviewWindowController) {
|
||||
webviews.removeValue(forKey: viewId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// MessageChannelPlugin.swift
|
||||
// desktop_webview_window
|
||||
//
|
||||
// Created by Bin Yang on 2021/11/19.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
public class ClientMessageChannelPlugin: NSObject, FlutterPlugin {
|
||||
public init(methodChannel: FlutterMethodChannel) {
|
||||
self.methodChannel = methodChannel
|
||||
super.init()
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "webview_message/client_channel", binaryMessenger: registrar.messenger)
|
||||
let instance = ClientMessageChannelPlugin(methodChannel: channel)
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
ServerMessageChannel.shared.addClient(client: instance)
|
||||
}
|
||||
|
||||
private let methodChannel: FlutterMethodChannel
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
ServerMessageChannel.shared.dispatchMethodCall(call: call, from: self)
|
||||
// this is a boardcast, so we complete this with sucess.
|
||||
result(nil)
|
||||
}
|
||||
|
||||
fileprivate func invokeMethod(_ call: FlutterMethodCall) {
|
||||
methodChannel.invokeMethod(call.method, arguments: call.arguments)
|
||||
}
|
||||
}
|
||||
|
||||
class ServerMessageChannel {
|
||||
static let shared: ServerMessageChannel = ServerMessageChannel()
|
||||
|
||||
private var clients: [ClientMessageChannelPlugin] = []
|
||||
|
||||
func addClient(client: ClientMessageChannelPlugin) {
|
||||
clients.append(client)
|
||||
}
|
||||
|
||||
func removeClient(client: ClientMessageChannelPlugin) {
|
||||
if let index = clients.firstIndex(of: client) {
|
||||
clients.remove(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
func dispatchMethodCall(call: FlutterMethodCall, from clientFrom: ClientMessageChannelPlugin) {
|
||||
for client in clients {
|
||||
if client != clientFrom {
|
||||
client.invokeMethod(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,248 +0,0 @@
|
|||
//
|
||||
// WebViewLayoutController.swift
|
||||
// desktop_webView_window
|
||||
//
|
||||
// Created by Bin Yang on 2021/11/18.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import WebKit
|
||||
|
||||
class WebViewLayoutController: NSViewController {
|
||||
private lazy var titleBarController: FlutterViewController = {
|
||||
let project = FlutterDartProject()
|
||||
project.dartEntrypointArguments = ["web_view_title_bar", "\(viewId)", "\(titleBarTopPadding)"]
|
||||
return FlutterViewController(project: project)
|
||||
}()
|
||||
|
||||
private lazy var webView: WKWebView = {
|
||||
WKWebView()
|
||||
}()
|
||||
|
||||
private var javaScriptHandlerNames: [String] = []
|
||||
|
||||
weak var webViewPlugin: DesktopWebviewWindowPlugin?
|
||||
|
||||
private var defaultUserAgent: String?
|
||||
|
||||
private let methodChannel: FlutterMethodChannel
|
||||
|
||||
private let viewId: Int64
|
||||
|
||||
private let titleBarHeight: Int
|
||||
|
||||
private let titleBarTopPadding: Int
|
||||
|
||||
public init(methodChannel: FlutterMethodChannel, viewId: Int64, titleBarHeight: Int, titleBarTopPadding: Int) {
|
||||
self.viewId = viewId
|
||||
self.methodChannel = methodChannel
|
||||
self.titleBarHeight = titleBarHeight
|
||||
self.titleBarTopPadding = titleBarTopPadding
|
||||
super.init(nibName: "WebViewLayoutController", bundle: Bundle(for: WebViewLayoutController.self))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
addChild(titleBarController)
|
||||
titleBarController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
// Register titlebar plugins
|
||||
ClientMessageChannelPlugin.register(with: titleBarController.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||
|
||||
let flutterView = titleBarController.view
|
||||
|
||||
flutterView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
view.addSubview(flutterView)
|
||||
|
||||
let constraints = [
|
||||
flutterView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
flutterView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
flutterView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
flutterView.heightAnchor.constraint(equalToConstant: CGFloat(titleBarHeight + titleBarTopPadding)),
|
||||
]
|
||||
|
||||
NSLayoutConstraint.activate(constraints)
|
||||
|
||||
view.addSubview(webView)
|
||||
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
webView.topAnchor.constraint(equalTo: flutterView.bottomAnchor),
|
||||
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
webView.navigationDelegate = self
|
||||
webView.uiDelegate = self
|
||||
|
||||
// TODO(boyan01) Make it configuable from flutter.
|
||||
webView.configuration.preferences.javaEnabled = true
|
||||
webView.configuration.preferences.minimumFontSize = 12
|
||||
webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
|
||||
webView.configuration.allowsAirPlayForMediaPlayback = true
|
||||
webView.configuration.mediaTypesRequiringUserActionForPlayback = .video
|
||||
|
||||
webView.addObserver(self, forKeyPath: "canGoBack", options: .new, context: nil)
|
||||
webView.addObserver(self, forKeyPath: "canGoForward", options: .new, context: nil)
|
||||
webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)
|
||||
|
||||
defaultUserAgent = webView.value(forKey: "userAgent") as? String
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if keyPath == "canGoBack" || keyPath == "canGoForward" {
|
||||
methodChannel.invokeMethod("onHistoryChanged", arguments: [
|
||||
"id": viewId,
|
||||
"canGoBack": webView.canGoBack,
|
||||
"canGoForward": webView.canGoForward,
|
||||
] as [String: Any])
|
||||
} else if keyPath == "loading" {
|
||||
if webView.isLoading {
|
||||
methodChannel.invokeMethod("onNavigationStarted", arguments: [
|
||||
"id": viewId,
|
||||
])
|
||||
} else {
|
||||
methodChannel.invokeMethod("onNavigationCompleted", arguments: [
|
||||
"id": viewId,
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func load(url: URL) {
|
||||
debugPrint("load url: \(url)")
|
||||
webView.load(URLRequest(url: url))
|
||||
}
|
||||
|
||||
func addJavascriptInterface(name: String) {
|
||||
javaScriptHandlerNames.append(name)
|
||||
webView.configuration.userContentController.add(self, name: name)
|
||||
}
|
||||
|
||||
func removeJavascriptInterface(name: String) {
|
||||
if let index = javaScriptHandlerNames.firstIndex(of: name) {
|
||||
javaScriptHandlerNames.remove(at: index)
|
||||
}
|
||||
webView.configuration.userContentController.removeScriptMessageHandler(forName: name)
|
||||
}
|
||||
|
||||
func addScriptToExecuteOnDocumentCreated(javaScript: String) {
|
||||
webView.configuration.userContentController.addUserScript(
|
||||
WKUserScript(source: javaScript, injectionTime: .atDocumentStart, forMainFrameOnly: true))
|
||||
}
|
||||
|
||||
func setApplicationNameForUserAgent(applicationName: String) {
|
||||
webView.customUserAgent = (defaultUserAgent ?? "") + applicationName
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
webView.stopLoading(self)
|
||||
webView.removeFromSuperview()
|
||||
titleBarController.engine.shutDownEngine()
|
||||
}
|
||||
|
||||
func reload() {
|
||||
webView.reload()
|
||||
}
|
||||
|
||||
func goBack() {
|
||||
if webView.canGoBack {
|
||||
webView.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
func goForward() {
|
||||
if webView.canGoForward {
|
||||
webView.goForward()
|
||||
}
|
||||
}
|
||||
|
||||
func stopLoading() {
|
||||
webView.stopLoading()
|
||||
}
|
||||
|
||||
func evaluateJavaScript(javaScriptString: String, completer: @escaping FlutterResult) {
|
||||
webView.evaluateJavaScript(javaScriptString) { result, error in
|
||||
if let error = error {
|
||||
completer(FlutterError(code: "1", message: error.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
completer(result)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if DEBUG
|
||||
print("\(self) deinited")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension WebViewLayoutController: WKNavigationDelegate {
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
guard let url = navigationAction.request.url else {
|
||||
decisionHandler(.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
guard ["http", "https", "file"].contains(url.scheme?.lowercased() ?? "") else {
|
||||
decisionHandler(.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
methodChannel.invokeMethod("onUrlRequested", arguments: [
|
||||
"id": viewId,
|
||||
"url": url.absoluteString,
|
||||
] as [String: Any])
|
||||
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
|
||||
extension WebViewLayoutController: WKUIDelegate {
|
||||
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
|
||||
methodChannel.invokeMethod(
|
||||
"runJavaScriptTextInputPanelWithPrompt",
|
||||
arguments: [
|
||||
"id": viewId,
|
||||
"prompt": prompt,
|
||||
"defaultText": defaultText ?? "",
|
||||
] as [String: Any]) { result in
|
||||
completionHandler((result as? String) ?? "")
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
|
||||
if !(navigationAction.targetFrame?.isMainFrame ?? false) {
|
||||
webView.load(navigationAction.request)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension WebViewLayoutController: WKScriptMessageHandler {
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
methodChannel.invokeMethod(
|
||||
"onJavaScriptMessage",
|
||||
arguments: [
|
||||
"id": viewId,
|
||||
"name": message.name,
|
||||
"body": message.body,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="WebViewLayoutController" customModule="desktop_webview_window" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="Hz6-mo-xeY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<point key="canvasLocation" x="139" y="147"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//
|
||||
// WebviewWindowController.swift
|
||||
// webview_window
|
||||
//
|
||||
// Created by Bin Yang on 2021/10/15.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import WebKit
|
||||
|
||||
class WebviewWindowController: NSWindowController {
|
||||
private let methodChannel: FlutterMethodChannel
|
||||
|
||||
private let viewId: Int64
|
||||
|
||||
private let width, height: Int
|
||||
|
||||
private let titleBarHeight: Int
|
||||
|
||||
private let titleBarTopPadding: Int
|
||||
|
||||
private let title: String
|
||||
|
||||
public weak var webviewPlugin: DesktopWebviewWindowPlugin?
|
||||
|
||||
init(viewId: Int64, methodChannel: FlutterMethodChannel,
|
||||
width: Int, height: Int,
|
||||
title: String, titleBarHeight: Int,
|
||||
titleBarTopPadding: Int) {
|
||||
self.viewId = viewId
|
||||
self.methodChannel = methodChannel
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.titleBarHeight = titleBarHeight
|
||||
self.titleBarTopPadding = titleBarTopPadding
|
||||
self.title = title
|
||||
super.init(window: nil)
|
||||
|
||||
let newWindow = NSWindow(contentRect: NSRect(x: 0, y: 0, width: width, height: height), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false)
|
||||
newWindow.delegate = self
|
||||
newWindow.title = title
|
||||
newWindow.titlebarAppearsTransparent = true
|
||||
|
||||
let contentViewController = WebViewLayoutController(
|
||||
methodChannel: methodChannel,
|
||||
viewId: viewId, titleBarHeight: titleBarHeight,
|
||||
titleBarTopPadding: titleBarTopPadding)
|
||||
newWindow.contentViewController = contentViewController
|
||||
newWindow.setContentSize(NSSize(width: width, height: height))
|
||||
newWindow.center()
|
||||
|
||||
window = newWindow
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public var webViewController: WebViewLayoutController {
|
||||
window?.contentViewController as! WebViewLayoutController
|
||||
}
|
||||
|
||||
override func keyDown(with event: NSEvent) {
|
||||
if event.charactersIgnoringModifiers == "w" && event.modifierFlags.contains(.command) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
webViewController.destroy()
|
||||
webviewPlugin = nil
|
||||
window?.delegate = nil
|
||||
window = nil
|
||||
}
|
||||
|
||||
func setAppearance(brightness: Int) {
|
||||
switch brightness {
|
||||
case 0:
|
||||
if #available(macOS 10.14, *) {
|
||||
window?.appearance = NSAppearance(named: .darkAqua)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
break
|
||||
case 1:
|
||||
window?.appearance = NSAppearance(named: .aqua)
|
||||
break
|
||||
default:
|
||||
window?.appearance = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if DEBUG
|
||||
print("\(self) deinited")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension WebviewWindowController: NSWindowDelegate {
|
||||
func windowWillClose(_ notification: Notification) {
|
||||
webViewController.destroy()
|
||||
methodChannel.invokeMethod("onWindowClose", arguments: ["id": viewId])
|
||||
webviewPlugin?.onWebviewWindowClose(viewId: viewId, wc: self)
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint webview_window.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'desktop_webview_window'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new flutter plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new flutter plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.12'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.4"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
sdks:
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
name: desktop_webview_window
|
||||
description: Show a webview window on your flutter desktop application.
|
||||
version: 0.2.3
|
||||
homepage: https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_webview_window
|
||||
|
||||
environment:
|
||||
sdk: ">=2.14.0 <4.0.0"
|
||||
flutter: ">=2.5.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^1.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.
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
macos:
|
||||
pluginClass: DesktopWebviewWindowPlugin
|
||||
|
|
@ -1 +0,0 @@
|
|||
python3 -m http.server 3000 --directory example/test_web_pages
|
||||
|
|
@ -1 +0,0 @@
|
|||
void main() {}
|
||||
|
|
@ -316,9 +316,11 @@ packages:
|
|||
desktop_webview_window:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "packages/desktop_webview_window"
|
||||
relative: true
|
||||
source: path
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "861cb573a6decb815741ed76e845fe05a3662c95"
|
||||
url: "https://github.com/kodjodevf/desktop_webview_window.git"
|
||||
source: git
|
||||
version: "0.2.3"
|
||||
directed_graph:
|
||||
dependency: transitive
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ dependencies:
|
|||
share_plus: ^9.0.0
|
||||
xpath_selector_html_parser: ^3.0.1
|
||||
desktop_webview_window:
|
||||
path: ./packages/desktop_webview_window
|
||||
git:
|
||||
url: https://github.com/kodjodevf/desktop_webview_window.git
|
||||
ref: main
|
||||
archive: ^3.6.1
|
||||
file_picker: ^8.0.5
|
||||
path_provider: ^2.1.3
|
||||
|
|
@ -101,6 +103,7 @@ flutter:
|
|||
|
||||
assets:
|
||||
- assets/
|
||||
- assets/fonts/
|
||||
- assets/trackers_icons/
|
||||
- assets/app_icons/
|
||||
flutter_launcher_icons:
|
||||
|
|
|
|||