반응형

firebase에서 firestore 사용 설정을 했으나 로컬에서 firebase를 초기화하는 시점에서 firestore를 생성하라는 에러가 발생하는 경우가 있습니다. 이유는 "기본 GCP 리소스 위치가" 설정되지 않아서 발생하는 문제입니다.

아래와 같은 에러가 발생하면 프로젝트 설정 > 일반 > 내 프로젝트에서 "기본 GCP 리소스 위치"를 선택해주시면 됩니다.

=== Firestore Setup

Error: It looks like you haven't used Cloud Firestore in this project before. Go to https://console.firebase.google.com/project/project-name/firestore to create your Cloud Firestore database.

Having trouble? Try firebase [command] --help
 mihyunnoh  ~/work/coding_study/howtomakehappy/project-name   main 

반응형
반응형

quasar/cli 설치는 아래 3가지 중 하나를 선택하여 진행합니다.

저는 npm을 이용했습니다.

$ yarn global add @quasar/cli
$ yarn create quasar

# or:

$ npm i -g @quasar/cli
$ npm init quasar

# or:

$ pnpm add -g @quasar/cli # experimental support
$ pnpm create quasar # experimental support

 

npm 명령어 및 로그

 mihyunnoh  ~/work/coding_study/howtomakehappy  npm i -g @quasar/cli   127 ↵

added 190 packages, and audited 191 packages in 10s

53 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 mihyunnoh  ~/work/coding_study/howtomakehappy 

 

"npm init quasar" 명령어로 프로젝트 생성 및 설정

 mihyunnoh  ~/work/coding_study/howtomakehappy  clear                      ✔
 mihyunnoh  ~/work/coding_study/howtomakehappy  npm init quasar            ✔
Need to install the following packages:
  create-quasar@1.1.0
Ok to proceed? (y) y


 .d88888b.
d88P" "Y88b
888     888
888     888 888  888  8888b.  .d8888b   8888b.  888d888
888     888 888  888     "88b 88K          "88b 888P"
888 Y8b 888 888  888 .d888888 "Y8888b. .d888888 888
Y88b.Y8b88P Y88b 888 888  888      X88 888  888 888
 "Y888888"   "Y88888 "Y888888  88888P' "Y888888 888
       Y8b

✔ What would you like to build? › App with Quasar CLI, let's go!
✔ Project folder: … new-handable-admin
✔ Pick Quasar version: › Quasar v2 (Vue 3 | latest and greatest)
✔ Pick script type: › Javascript
✔ Pick Quasar App CLI variant: › Quasar App CLI with Webpack
✔ Package name: … new-handable-admin
✔ Project product name: (must start with letter if building mobile apps) … newHandableAdmin
✔ Project description: … A Quasar Project
✔ Author: … HowToMakeHappy <howtomakehappy.com@gmail.com>
✔ Pick your CSS preprocessor: › Sass with SCSS syntax
✔ Check the features needed for your project: › ESLint, State Management (Pinia)
✔ Pick an ESLint preset: › Standard

 Quasar • Generating files...

 - README.md
 ..............
 - src/stores/store-flag.d.ts

 Quasar •  SUCCESS  • The project has been scaffolded

✔ Install project dependencies? (recommended) › Yes, use npm

npm WARN deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead

added 998 packages, and audited 999 packages in 44s

165 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities



> new-handable-admin@0.0.1 lint
> eslint --ext .js,.vue ./ --fix



To get started:

  cd new-handable-admin
  quasar dev # or: yarn quasar dev # or: npx quasar dev

Documentation can be found at: https://v2.quasar.dev

Quasar is relying on donations to evolve. We'd be very grateful if you can
read our manifest on "Why donations are important": https://v2.quasar.dev/why-donate
Donation campaign: https://donate.quasar.dev
Any amount is very welcome.
If invoices are required, please first contact Razvan Stoenescu.

Please give us a star on Github if you appreciate our work:
  https://github.com/quasarframework/quasar

Enjoy! - Quasar Team

 mihyunnoh  ~/work/coding_study/howtomakehappy  quasar dev                 ✔


 .d88888b.
d88P" "Y88b
888     888
888     888 888  888  8888b.  .d8888b   8888b.  888d888
888     888 888  888     "88b 88K          "88b 888P"
888 Y8b 888 888  888 .d888888 "Y8888b. .d888888 888
Y88b.Y8b88P Y88b 888 888  888      X88 888  888 888
 "Y888888"   "Y88888 "Y888888  88888P' "Y888888 888
       Y8b

  Running @quasar/cli v2.0.0

  Example usage
    $ quasar <command> <options>

  Help for a command
    $ quasar <command> --help
    $ quasar <command> -h

  Options
    --version, -v Print Quasar CLI version

  Commands
    info      Display info about your machine
                   (and your App if in a project folder)
    upgrade   Check (and optionally) upgrade Quasar packages
                   from a Quasar project folder
    serve     Create an ad-hoc server on App's distributables
    help, -h  Displays this message

  --------------
  => IMPORTANT !
  => Trigger this inside of a Quasar project (and npm/yarn install) for more commands.
  --------------


 Error Unknown command "dev"

 

하지만 마지막에 "quasar dev"로 실행하려했으나 실패했습니다. 확인을 해보니 폴더가 잘못되어있네요.

 Error Unknown command "dev"

명령이 안먹었는데 프로젝트 폴더로 이동해서 실행하지 않고 부모 폴더에서 실행했기때문에 발생한 문제였습니다. 로그를 살펴보니 아래와 같이 직전에 생성한 프로젝트 폴더로 이동한 후 "quasar dev"를 실행하라고 명시되어있습니다.

To get started:

  cd new-handable-admin
  quasar dev # or: yarn quasar dev # or: npx quasar dev

 

 

출처

https://quasar.dev/start/quasar-cli

반응형
반응형

hint

입력이 없는 상태에서 사용자에게 안내하는 메시지입니다.

TextField(
  decoration: InputDecoration(
    hintText: 'name',
  ),
)

 

focusedBorder

입력창에 커서가 있는 경우의 입력창 밑줄을 꾸미는데 사용됩니다.

focusedBorder: OutlineInputBorder(
  borderSide: BorderSide(
    width: 1,
    color: Colors.green,
  ),
)

 

enabledBorder

입력창이 enable이며 focus가 없는 경우의 입력창의 밑줄을 꾸미는데 사용됩니다.

enabledBorder: OutlineInputBorder(
  borderSide: BorderSide(
    width: 1,
    color: Colors.green,
  ),
)

 

OutlineInputBorder

UnderlineInputBorder을 OutlineInputBorder으로 변경시 밑줄이 테두리로 변경됩니다.

enabledBorder: UnderlineInputBorder(
  borderSide: BorderSide(
    width: 1,
    color: Colors.green,
  ),
)

 

입력값 사용하기

입력된 값을 화면에서 사용하려면 onChanged를 구현해야 합니다.

// 멤버변수
String _title = '';

// 위젯
TextField(
  onChanged: (text) {
    _title = text;
  },
)
반응형
반응형

Container의 테두리 중 아래와 같이

맨 밑에만 색상을 주는 경우가 있습니다.

 

 

 

이때는 Container의  decoration의

border.bottom에 색상 밑 두께를 지정하면 됩니다.

(다른 방향은 left, top, right를 쓰면 됩니다.)

 

Container(
  decoration: const BoxDecoration(
    border: Border(
      bottom: BorderSide(
        color: Colors.black,
        width: 1.0,
      ),
    ),
  ),
)

 

전체소스

Container(
  decoration: const BoxDecoration(
    border: Border(
      bottom: BorderSide(
        color: Colors.black,
        width: 1.0,
      ),
    ),
  ),
  width: double.infinity,
  height: 50,
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      Container(
        margin: const EdgeInsets.only(left: 15),
        child: const Text('로그아웃'),
      ),
      Container(
        margin: const EdgeInsets.only(right: 15),
        child: const Icon(Icons.settings),
      ),
    ],
  ),
)
반응형
반응형

에뮬레이터는 Quick Boot가 기본값입니다.

빠르게 부팅되는 장점이 있지만 AVD에 문제가 있는 경우

다시 시작해도 동일한 문제가 재현되기에  불편하기도 합니다.

Cold Boot는 전원을 완전 종료 후 부팅한 효과를 볼 수 있습니다.

부팅이 느린 단점이 있겠죠?

 

설정방법은 아래와 같습니다.

 

1. Device Manager

2. Edit this AVD

 

 

3. 고급설정

 

4. Quick에서 Cold로 변경

5. Finish

반응형
반응형

google sign in 사용시

한번 로그인을 하면 이후에는 이전 계정으로

바로 로그인이 되는 경우가 있다.

 

다른 계정으로 테스트하려는데

그냥 로그인이 되어버려서 난감하다.

 

바로 로그인 되는 코드는 아래와 같다.

final googleSignIn = GoogleSignIn();

 

아래와 같이 옵션을 추가하면

매번 계정을 선택하는 방식으로 변경된다.

final googleSignIn = GoogleSignIn(scopes: ['email', 'profile']);

 

전체소스

Future<UserCredential> signInWithGoogle() async {
  final googleSignIn = GoogleSignIn(scopes: ['email', 'profile']);

  final GoogleSignInAccount? googleUser = await googleSignIn.signIn();

  final GoogleSignInAuthentication? googleAuth =
      await googleUser?.authentication;

  final credential = GoogleAuthProvider.credential(
    accessToken: googleAuth?.accessToken,
    idToken: googleAuth?.idToken,
  );

  return await FirebaseAuth.instance.signInWithCredential(credential);
}

 

반응형

'flutter' 카테고리의 다른 글

[flutter] TextField  (0) 2023.01.25
[flutter] Container Border bottom line  (0) 2023.01.20
플러터로 첫코딩하기! 22- Button의 종류들  (0) 2023.01.03
[flutter] flutter에서 SQLite 사용하기  (0) 2023.01.02
[dart] List.generate  (0) 2023.01.01
반응형

현재 Flutter에서 제공하는 기본적인 버튼은 총 3가지입니다.

 

1.ElevatedButton

2.OutlinedButton

3.TextButton 

 

기본적인 틀은 아래와 같습니다. 

여기서 styleform으로 원하는 모양의 버튼을 만들 수 있습니다. 

 

그리고 Container도 버튼이 될 수 있습니다. 

 GestureDetector를 Container를 감싸주어서 

ontap:  파라미터를 이용해서 버튼으로 만들 수 있습니다. 

 

Flutter에서 제공하는 버튼이 마음에 안든다면 Container로 버튼을 만들어서 사용하는 것도 좋은 방법입니다! 

 

 

 

 

 

import 'package:flutter/material.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('버튼 예제'),),
      body: Center(
        child: Column(mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(onPressed: (){}, child: Text('ElveatedButton')),
            OutlinedButton(onPressed: (){}, child: Text('OutlinedButton')),
            TextButton(onPressed: (){}, child: Text('TextButton')),
            GestureDetector(
              onTap: (){},
              child: Container(height: 50,
              width: 100,child: Text('Container Button'),),
            ),
          ],
        ),
      ),
    );
  }
}

다음시간에는 이 버튼을 이용해서 페이지 이동을 진행해보겠습니다 

반응형
반응형

패키지 설치

 

1. sqflite

flutter pub add sqflite

https://pub.dev/packages/sqflite/install

 

2. path_provider

flutter pub add path_provider

https://pub.dev/packages/path_provider/install

 

3. path

flutter pub add path

https://pub.dev/packages/path/install

 

 

user.dart

class User {
  int? userId;
  String displayName;

  User({this.userId, required this.displayName});
}

 

db_helper.dart

import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_test/user.dart';

const String tableName = 'Users';

class DBHelper {
  DBHelper._();
  static final DBHelper _db = DBHelper._();
  factory DBHelper() => _db;

  static Database? _database;

  Future<Database> get database async {
    if (_database != null) return _database!;

    _database = await initDB();
    return _database!;
  }

  initDB() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'UserInfoDB.db');

    return await openDatabase(path, version: 1, onCreate: (db, version) async {
      await db.execute('''
          CREATE TABLE $tableName (
            userId INTEGER PRIMARY KEY,
            displayName TEXT
          )
        ''');
    }, onUpgrade: (db, oldVersion, newVersion) {});
  }

  createData(User user) async {
    final db = await database;
    var res = await db.rawInsert(
        'INSERT INTO $tableName(displayName) VALUES(?)', [user.displayName]);
    return res;
  }

  getUser(int userId) async {
    final db = await database;
    var res = await db
        .rawQuery('SELECT * FROM $tableName WHERE userId = ?', [userId]);
    return res.isNotEmpty
        ? User(
            userId: res.first['userId'] as int,
            displayName: res.first['displayName'] as String)
        : Null;
  }

  Future<List<User>> getAllUsers() async {
    final db = await database;
    var res = await db.rawQuery('SELECT * FROM $tableName');
    List<User> list = res.isNotEmpty
        ? res
            .map((c) => User(
                userId: c['userId'] as int,
                displayName: c['displayName'] as String))
            .toList()
        : [];

    return list;
  }

  deleteUser(int userId) async {
    final db = await database;
    var res = db.rawDelete('DELETE FROM $tableName WHERE userId = ?', [userId]);
    return res;
  }

  deleteAllUsers() async {
    final db = await database;
    db.rawDelete('DELETE FROM $tableName');
  }

  updateUser(User user) async {
    final db = await database;
    var res = db.rawUpdate(
        'UPDATE $tableName SET displayName = ? WHERE userId = ?',
        [user.displayName, user.userId]);
    return res;
  }
}

 

main.dart

import 'package:flutter/material.dart';
import 'package:sqflite_test/add_page.dart';
import 'package:sqflite_test/edit_page.dart';
import 'package:sqflite_test/home_page.dart';

void main() {
  runApp(MaterialApp(
    initialRoute: '/',
    routes: {
      '/': (context) => const HomePage(),
      '/add': (context) => const AddPage(),
      '/edit': (context) => const EditPage(),
    },
  ));
}

 

home_page.dart

import 'package:flutter/material.dart';
import 'package:sqflite_test/User.dart';
import 'package:sqflite_test/db_helper.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SQLite'),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () async {
          dynamic ret = await Navigator.pushNamed(context, '/add');
          if (ret != null) {
            User user = ret as User;
            DBHelper().createData(user);
            setState(() {});
          }
        },
      ),
      body: FutureBuilder(
        future: DBHelper().getAllUsers(),
        builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data!.length,
              itemBuilder: (BuildContext context, int index) {
                User item = snapshot.data![index];

                return Card(
                  child: ListTile(
                    onTap: () async {
                      await Navigator.pushNamed(context, '/edit',
                          arguments: item);
                      setState(() {});
                    },
                    onLongPress: () async {
                      await DBHelper().deleteUser(item.userId!);
                      setState(() {});
                    },
                    title: Text(item.displayName),
                  ),
                );
              },
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }
}

 

 

add_page.dart

import 'package:flutter/material.dart';
import 'package:sqflite_test/User.dart';

class AddPage extends StatefulWidget {
  const AddPage({Key? key}) : super(key: key);

  @override
  State<AddPage> createState() => _AddPageState();
}

class _AddPageState extends State<AddPage> {
  final TextEditingController _textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('추가'),
      ),
      body: Column(
        children: [
          TextField(
            controller: _textController,
            decoration: const InputDecoration(
              hintText: '이름을 입력하세요.',
              border: OutlineInputBorder(), //외곽선
            ),
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.pop(context, User(displayName: _textController.text));
              },
              child: const Text('완료')),
        ],
      ),
    );
  }
}

 

edit_page.dart

import 'package:flutter/material.dart';
import 'package:sqflite_test/User.dart';
import 'package:sqflite_test/db_helper.dart';

class EditPage extends StatefulWidget {
  const EditPage({Key? key}) : super(key: key);

  @override
  State<EditPage> createState() => _EditPageState();
}

class _EditPageState extends State<EditPage> {
  final TextEditingController _textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    User user = ModalRoute.of(context)!.settings.arguments as User;
    _textController.text = user.displayName;

    return Scaffold(
      appBar: AppBar(
        title: const Text('수정'),
      ),
      body: Column(
        children: [
          TextField(
            controller: _textController,
            decoration: const InputDecoration(
              hintText: '이름을 입력하세요.',
              border: OutlineInputBorder(), //외곽선
            ),
          ),
          ElevatedButton(
              onPressed: () {
                user.displayName = _textController.text;
                DBHelper().updateUser(user);
                Navigator.pop(context);
              },
              child: const Text('수정')),
        ],
      ),
    );
  }
}
반응형

+ Recent posts