mac 下快速切换flutter sdk版本的小工具实现

882 阅读2分钟

一分钟配置, 每次切换只需两次点击即可全局切换flutter sdk 下面是工具截图: 小工具形式: image.png

面板形式: image.png

主要实现思路 调整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 `

download.csdn.net/download/an…