一分钟配置,
每次切换只需两次点击即可全局切换flutter sdk
下面是工具截图:
小工具形式:
面板形式:
主要实现思路 调整flutter sdk 配置的固定路径所对应的flutter sdk文件,不调整路径.以达到切换flutter sdk的目的. 主要是为了防止频繁切换配置路径导致各种开发工具和配置对应的flutter sdk失效. 以下为实现代码: 最下面有可运行的小工具文件,可以直接运行.
` \
#import "ViewController.h"
#import "Masonry.h"
\
static NSString *flutterSDKPathKey = @"flutterSDKPathKey";
static NSString *pathHolderString = @"请修改.bash_profile文件夹flutter路径为:\n%@/flutter/bin:$PATH";
@interface ViewController()<NSTableViewDelegate,NSTableViewDataSource>
@property(nonatomic, strong) NSStatusItem *statusItem;
\
@property (nonatomic, copy) NSString *filepath;
@property (nonatomic, strong) NSTextField *textField;
@property (nonatomic, strong) NSTableView *flutterSDKTableView;
@property (nonatomic, copy) NSArray *flutterSDKArray;
@property (nonatomic, copy) NSString *currentFlutterSDKName;
@property (nonatomic, strong) NSTextField *patchExpainTextField;
@end
\
@implementation ViewController
\
- (void)viewDidLoad {
[super viewDidLoad];
NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
NSString *path = [userDef valueForKey:flutterSDKPathKey];
NSTextField *expainTextField = [[NSTextField alloc] init];
expainTextField.editable = false;
expainTextField.bordered = false;
[self.view addSubview:expainTextField];
[expainTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.mas_equalTo(self.view);
make.height.mas_equalTo(30);
}];
self.textField = [[NSTextField alloc] init];
self.textField.editable = false;
self.textField.bordered = false;
[self.view addSubview:self.textField];
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(expainTextField.mas_bottom);
make.left.mas_equalTo(self.view);
make.height.mas_equalTo(30);
make.right.mas_equalTo(self.view).mas_offset(-60);
}];
NSButton *choosePathBtn = [[NSButton alloc] init];
[choosePathBtn setTitle:@"选择"];
[choosePathBtn setTarget:self];
[choosePathBtn setAction: @selector(choosePathBtnClick:)];
[self.view addSubview:choosePathBtn];
[choosePathBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.textField.mas_right);
make.bottom.top.mas_equalTo(self.textField);
make.right.mas_equalTo(self.view);
}];
self.patchExpainTextField = [[NSTextField alloc] init];
self.patchExpainTextField.editable = false;
self.patchExpainTextField.bordered = false;
[self.view addSubview:self.patchExpainTextField];
[self.patchExpainTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.textField.mas_bottom);
make.left.right.mas_equalTo(self.view);
}];
if(path != NULL){
expainTextField.stringValue = @"FlutterSDK文件夹的地址";
self.textField.placeholderString = path;
self.filepath = path;
self.patchExpainTextField.stringValue = [NSString stringWithFormat:pathHolderString,path];
}else{
expainTextField.stringValue = @"请选择存放FlutterSDK的文件夹";
self.textField.placeholderString = @"";
}
NSButton *trueBtn = [[NSButton alloc] init];
[trueBtn setTitle:@"确定"];
[trueBtn setTarget:self];
[trueBtn setAction: @selector(trueBtnClick:)];
[self.view addSubview:trueBtn];
[trueBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.mas_equalTo(self.view);
make.height.mas_equalTo(40);
}];
self.flutterSDKTableView = [[NSTableView alloc] init];
self.flutterSDKTableView.delegate = self;
self.flutterSDKTableView.dataSource = self;
self.flutterSDKTableView.backgroundColor = [NSColor grayColor];
NSTableColumn *column = [[NSTableColumn alloc]initWithIdentifier:@"CTableColumn"];
[self.flutterSDKTableView addTableColumn:column];
[self.view addSubview:self.flutterSDKTableView];
[self.flutterSDKTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(self.view);
make.top.mas_equalTo(self.patchExpainTextField.mas_bottom);
make.bottom.mas_equalTo(trueBtn.mas_top);
}];
if(self.filepath != NULL){
self.currentFlutterSDKName = [self currentFlutterSDKVersionInPath:self.filepath];
self.flutterSDKArray = [self searchFlutterSDKDirInPath:self.filepath];
[self.flutterSDKTableView reloadData];
[self setCustomMenuWithFlutterSDKVersion:self.flutterSDKArray];
}
}
- (void)choosePathBtnClick:(id)sender{
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
[oPanel setCanChooseDirectories:YES];
[oPanel setCanChooseFiles:false];
[oPanel setDirectoryURL:[NSURL URLWithString:NSHomeDirectory()]];
if([oPanel runModal] == NSModalResponseOK){
NSString *path = [oPanel URL].relativePath;
self.filepath = path;
[self.textField setStringValue:path];
}
//检查选择的文件夹下符合标准的FlutterSDK文件夹
self.patchExpainTextField.stringValue = [NSString stringWithFormat:pathHolderString,self.filepath];
self.currentFlutterSDKName = [self currentFlutterSDKVersionInPath:self.filepath];
self.flutterSDKArray = [self searchFlutterSDKDirInPath:self.filepath];
[self.flutterSDKTableView reloadData];
[self setCustomMenuWithFlutterSDKVersion:self.flutterSDKArray];
}
-(void)trueBtnClick:(id)sender{
if(self.filepath != NULL ){
NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
[userDef setValue:self.filepath forKey:flutterSDKPathKey];
[NSApp setAccessibilityHidden:true];
}
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
\
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
NSView *cellView = [[NSView alloc] init];
[cellView setWantsLayer:YES];
\
NSTextField *cellText = [[NSTextField alloc] init];
cellText.editable = false;
cellText.backgroundColor = [NSColor clearColor];
cellText.bordered = false;
NSString *cellString = self.flutterSDKArray[row];
cellText.stringValue = cellString;
[cellView addSubview:cellText];
[cellText mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(cellView);
}];
if([cellString isEqualToString:self.currentFlutterSDKName]){
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:false];
[self.statusItem.button setTitle:[NSString stringWithFormat:@"%@",self.currentFlutterSDKName]];
}
return cellView;
}
-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{
return 40;
}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return self.flutterSDKArray.count;
}
-(BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row{
NSString *flutterSDKName = self.flutterSDKArray[row];
self.currentFlutterSDKName = flutterSDKName;
[self useFlutterSDKVersion:flutterSDKName];
[self.statusItem.button setTitle:[NSString stringWithFormat:@"%@",self.currentFlutterSDKName]];
return YES;
}
\
-(void)useFlutterSDKVersion:(NSString *)sdkName{
//遍历,将现有flutter挪回原处
NSFileManager *fManager = [NSFileManager defaultManager];
BOOL isDir;
BOOL isExit = [fManager fileExistsAtPath:self.filepath isDirectory:&isDir];
//是否已经有在外面的flutter
BOOL isFlutterExit = [fManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@",self.filepath,@"flutter"] isDirectory:&isDir];
if(isExit&&isDir&&isFlutterExit){
NSError *error;
NSArray *fileArray = [fManager contentsOfDirectoryAtPath:self.filepath error:&error];
NSString regex = @"^([0-9]).([0-9])*.([0-9])+$";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
for(NSString *fileName in fileArray){
//是否符合sdk命名标准
BOOL result = [predicate evaluateWithObject:fileName];
if(result){
BOOL isDir;
BOOL flutterIsExit = [fManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@/%@",self.filepath,fileName,@"flutter"] isDirectory:&isDir];
if(!flutterIsExit){
//该版本下没有flutter,将flutter挪入
NSString *outFlutterPath = [NSString stringWithFormat:@"%@/%@",self.filepath,@"flutter"];
NSString *innerFlutterPath = [NSString stringWithFormat:@"%@/%@/%@",self.filepath,fileName,@"flutter"];
NSError *error;
BOOL success = [fManager moveItemAtPath:outFlutterPath toPath:innerFlutterPath error:&error];
if(!success){
NSLog(@"挪动异常:%@",error);
return;
}
}
}
}
}
//将选中的sdk挪出来
NSString *outFlutterPath = [NSString stringWithFormat:@"%@/%@",self.filepath,@"flutter"];
NSString *innerFlutterPath = [NSString stringWithFormat:@"%@/%@/%@",self.filepath,sdkName,@"flutter"];
NSError *error;
BOOL success = [fManager moveItemAtPath:innerFlutterPath toPath:outFlutterPath error:&error];
NSLog(@"结果:%@",success?@"成功":[error description]);
}
\
//检查符合的文件夹
-(NSArray *)searchFlutterSDKDirInPath:(NSString *)path{
NSFileManager *fManager = [NSFileManager defaultManager];
NSError *error;
NSArray *fileArray = [fManager contentsOfDirectoryAtPath:path error:&error];
\
NSString regex = @"^([0-9]).([0-9])*.([0-9])+$";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
NSMutableArray *flutterSDKDirArray = [[NSMutableArray alloc] init];
for(NSString *fileName in fileArray){
BOOL result = [predicate evaluateWithObject:fileName];
if(result){
[flutterSDKDirArray addObject:fileName];
}
}
[flutterSDKDirArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
return [flutterSDKDirArray copy];
}
//检查目前使用的flutterSDK版本
- (NSString *)currentFlutterSDKVersionInPath:(NSString *)path{
//遍历,将现有flutter挪回原处
NSFileManager *fManager = [NSFileManager defaultManager];
BOOL isDir;
BOOL isExit = [fManager fileExistsAtPath:self.filepath isDirectory:&isDir];
//是否已经有在外面的flutter
BOOL isFlutterExit = [fManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@",self.filepath,@"flutter"] isDirectory:&isDir];
if(isExit&&isDir&&isFlutterExit){
NSError *error;
NSArray *fileArray = [fManager contentsOfDirectoryAtPath:self.filepath error:&error];
NSString regex = @"^([0-9]).([0-9])*.([0-9])+$";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
for(NSString *fileName in fileArray){
//是否符合sdk命名标准
BOOL result = [predicate evaluateWithObject:fileName];
if(result){
BOOL isDir;
BOOL flutterIsExit = [fManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@/%@",self.filepath,fileName,@"flutter"] isDirectory:&isDir];
if(!flutterIsExit){
return fileName;
}
}
}
return NULL;
}else{
return NULL;
}
}
//NSPopover
-(void)setCustomMenuWithFlutterSDKVersion:(NSArray *)flutterSDKNameArray{
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem.button setImage:[NSImage imageNamed:@"statusIcon"]];
[self.statusItem.button setTitle:[NSString stringWithFormat:@"%@",self.currentFlutterSDKName]];
[self.statusItem.button setBordered:false];
[self.statusItem.button setWantsLayer:true];
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"我是menu"];
for(int i = 0; i < flutterSDKNameArray.count; i++){
NSString *sdkName = flutterSDKNameArray[i];
NSMenuItem *item = [[NSMenuItem alloc]initWithTitle:sdkName action: @selector(itemClick:) keyEquivalent:@""];
item.target = self;
[menu addItem:item];
}
NSMenuItem *openApplicationItem = [[NSMenuItem alloc]initWithTitle:@"唤起面板" action: @selector(openApplicationClick:) keyEquivalent:@""];
openApplicationItem.target = self;
[menu addItem:openApplicationItem];
NSMenuItem *closeApplicationItem = [[NSMenuItem alloc]initWithTitle:@"退出" action: @selector(closeApplicationItemClick:) keyEquivalent:@""];
closeApplicationItem.target = self;
[menu addItem:closeApplicationItem];
self.statusItem.menu = menu;
}
-(void)itemClick:(id)sender{
NSMenuItem *item = (NSMenuItem *)sender;
NSLog(@"点击:%@",item.title);
self.currentFlutterSDKName = item.title;
[self useFlutterSDKVersion:item.title];
[self.flutterSDKTableView reloadData];
[self.statusItem.button setTitle:[NSString stringWithFormat:@"%@",self.currentFlutterSDKName]];
}
-(void)openApplicationClick:(id)sender{
[NSApp setAccessibilityHidden:false];
[NSApp setAccessibilityFrontmost:true];
}
-(void)closeApplicationItemClick:(id)sender{
[NSApp terminate:self];
}
@end `