Flutter - 保存用户数据

Flutter - Saving user data

我目前正在创建一个跟踪一般加密数据的应用程序,除了这些数据,我们还 post 为加密爱好者量身定制的新闻文章。此新闻文章数据的每个方面都以字符串形式存储,从图像 url 到发布日期 - 下面的完整列表

我正在寻找将此数据保存到用户设备的方法。在完美的情况下,我只是将这些数据保存在 JSON 数组中,但除了不知道如何执行此操作之外,我不确定这是否是保存此数据以供以后显示的最有效方法。

如果您认为 JSON 是保存这些数据的最佳方式,我需要知道的是如何将这些数据正确地管理到一系列不同的已保存文章中,以及如何将其正确地导入到我的飞镖代码。

最好有此代码的示例,我希望在新年前发布此应用程序,因此我需要所有可能的帮助。非常感谢。

这是我要保存/显示的上述数据 this source:

编辑:尝试修改 shadowsheep 的答案以适应索引模型

每个新闻小部件都是一个新的 inkwell,允许构建新的 scaffold。在此 scaffold 中,您会看到保存文章的选项。保存时,代码当前仅更改以下字符串的值 titledescriptionURLImage URL.

我非常想要一种将数据库(如 shadowsheep 所述)保存到用户设备的方法。这意味着即使用户关闭和打开应用程序,已保存的文章也将永久保留在设备上。

以下代码是我显示新闻数据的确切用例。

CarouselSlider(
 items: [1,2,3,4,5,6,7,8,9,10].map((index) {
    return  Builder(
      builder: (BuildContext context) {
        return Padding(
          padding:  EdgeInsets.only(
            top: 5.0,
            bottom: 20.0,
          ),
          child:  InkWell(
            borderRadius:  BorderRadius.only(
              topLeft: const Radius.circular(15.0),
              topRight: const Radius.circular(15.0),
            ),
            onTap: () {
              print('Opened article scaffold: "' + articles[index].title + "\"");
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) =>  Scaffold(
                    resizeToAvoidBottomPadding: false,
                    appBar:  AppBar(
                      backgroundColor: const Color(0xFF273A48),
                      elevation: 0.0,
                      title: Container(
                        width: _width*0.90,
                        height: 30,
                        padding: const EdgeInsets.only(
                          bottom: 5,
                          top: 5,
                          left: 10,
                          right: 10,
                        ),
                        decoration:  BoxDecoration(
                          color: Colors.white,
                          borderRadius:  BorderRadius.all(
                              Radius.circular(10.0),
                          ),
                        ),
                        alignment: Alignment.center,
                        child:  AutoSizeText(
                          'Published ' +  DateFormat.yMMMd().format(DateTime.parse(articles[index].publishedAt)) + ", "+ DateFormat.jm().format(DateTime.parse(articles[index].publishedAt)),
                          overflow: TextOverflow.ellipsis,
                          maxLines: 1,
                          minFontSize: 5,
                          maxFontSize: 20,
                          textAlign: TextAlign.center,
                          style:  TextStyle(
                            color: Colors.black,
                            fontFamily: 'Poppins',
                          ),
                        ),
                      ),
                    ),
                    body: Center(
                      child:  Scaffold(
                        resizeToAvoidBottomPadding: false,
                        body: Center(
                          child: Container(
                            decoration:  BoxDecoration(
                              gradient:  LinearGradient(
                                begin: Alignment.topCenter,
                                end: Alignment.bottomCenter,
                                colors: [
                                  const Color(0xFF273A48),
                                  Colors.blueGrey
                                ],
                              ),
                            ),
                            padding: const EdgeInsets.only(
                              top: 20,
                              left: 10,
                              right: 10,
                              bottom: 50
                            ),
                            child:  Column(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: <Widget>[
                                  Column(
                                  mainAxisAlignment: MainAxisAlignment.start,
                                  children: <Widget>[
                                      FutureBuilder<Null>(future: _launched, builder: _launchStatus),
                                      AutoSizeText(
                                      articles[index].title,
                                      overflow: TextOverflow.ellipsis,
                                      textAlign: TextAlign.center,
                                      maxFontSize: 30,
                                      minFontSize: 15,
                                      maxLines: 3,
                                      style:  TextStyle(
                                        color: Colors.white,
                                        fontFamily: 'Poppins',
                                      ),
                                    ),
                                      Divider(
                                      color: Colors.transparent,
                                      height: 15.0,
                                    ),
                                    Container(
                                      decoration:  BoxDecoration(
                                        borderRadius:  BorderRadius.circular(15.0),
                                        color: Colors.transparent,
                                        boxShadow: [
                                            BoxShadow(
                                            color: Colors.black.withAlpha(70),
                                            blurRadius: 50.0,
                                          )
                                        ],
                                        image:  DecorationImage(
                                          image:  NetworkImage(articles[index].urlToImage),
                                          fit: BoxFit.fitHeight,
                                        ),
                                      ),
                                      height: 220,
                                      width: 317.5,
                                    ),
                                      Divider(
                                      color: Colors.transparent,
                                      height: 15.0,
                                    ),
                                  ],
                                ),
                                Container(
                                  padding: const EdgeInsets.only(
                                    left: 20,
                                    right: 20
                                  ),
                                  decoration:  BoxDecoration(
                                    color: Colors.transparent,
                                    borderRadius:  BorderRadius.all(
                                        Radius.circular(10.0),
                                    ),
                                  ),
                                  child:  AutoSizeText(
                                    articles[index].description,
                                    overflow: TextOverflow.ellipsis,
                                    textAlign: TextAlign.center,
                                    maxFontSize: 30,
                                    minFontSize: 10,
                                    maxLines: 10,
                                    style:  TextStyle(
                                      color: Colors.white,
                                      fontFamily: 'Poppins',
                                    ),
                                  ),
                                  width: _width*0.90,
                                  height: _height*0.20,
                                ),
                                Container(
                                  padding: const EdgeInsets.all(4.0),
                                  decoration:  BoxDecoration(
                                    color: const Color(0xFF273A48),
                                    borderRadius:  BorderRadius.all(
                                        Radius.circular(10.0),
                                    ),
                                  ),
                                  child: Row(
                                    mainAxisSize: MainAxisSize.min,
                                    mainAxisAlignment: MainAxisAlignment.center,
                                    children: <Widget>[
                                        IconButton(
                                        icon:  Icon(
                                          Icons.favorite_border,
                                          color: Colors.red
                                        ),
                                        iconSize: 35.0,
                                        onPressed: () {
                                          _sTitle = articles[index].title;
                                          _sDescription = articles[index].description;
                                          _sURL = articles[index].url;
                                          _sURLtoImage = articles[index].urlToImage;
                                          Navigator.push(
                                          context,
                                          MaterialPageRoute(builder: (context) => _favoritesScreen())
                                          );
                                        }
                                      ),
                                        IconButton(
                                        icon:  Icon(
                                          Icons.mobile_screen_share,
                                          color: Colors.white,
                                        ),
                                        iconSize: 35.0,
                                        onPressed: () {
                                          Share.share(
                                            articles[index].title + "\n\nCheck out this article at:\n" + articles[index].url + "\n\nLearn more with Cryp - Tick Exchange",
                                          );
                                        }
                                      ),
                                        IconButton(
                                        icon:  Icon(Icons.launch, color: Colors.lightBlueAccent),
                                        iconSize: 32.5,
                                        onPressed: () => setState(() { _launched = _launchInWebViewOrVC(articles[index].url);}),
                                      ),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ),
                  )
                )
              );
            },
            child: Container(
              decoration:  BoxDecoration(
                borderRadius:  BorderRadius.circular(10.0),
                color: const Color(0xFF273A48),
                boxShadow: [
                    BoxShadow(
                    color: Colors.black.withAlpha(70),
                    offset: const Offset(5.0, 5.0),
                    blurRadius: 12.5,
                  )
                ],
                image:  DecorationImage(
                  alignment: Alignment.topCenter,
                  image:  NetworkImage(articles[index].urlToImage),
                  fit: BoxFit.cover,
                ),
              ),
              height: _height*0.35,
              width: _width*0.725,
              child:  Stack(
                children: <Widget>[
                    Align(
                    alignment: Alignment.bottomCenter,
                    child:  Stack(
                      alignment: Alignment.bottomRight,
                      children: <Widget>[
                        Container(
                          padding:  EdgeInsets.only(left: 10.0, right: 10.0),
                          decoration:  BoxDecoration(
                            color: const Color(0xFF273A48),
                            borderRadius:  BorderRadius.only(
                              bottomLeft:  Radius.circular(10.0),
                              bottomRight:  Radius.circular(10.0)
                            ),
                          ),
                          height: 60.0,
                          child:  Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: <Widget>[
                                Flexible(
                                child: Container(
                                  width: _width*0.725,
                                  child:  Text(
                                    articles[index].title,
                                    textAlign: TextAlign.center,
                                    overflow: TextOverflow.ellipsis,
                                    maxLines: 2,
                                    style:  TextStyle(
                                      color: Colors.white,
                                      fontFamily: 'Poppins',
                                      ),
                                    ),
                                ),
                              ),
                                Text(
                                'Published ' +  DateFormat.yMMMd().format(DateTime.parse(articles[index].publishedAt)) + ", "+ DateFormat.jm().format(DateTime.parse(articles[index].publishedAt)),
                                overflow: TextOverflow.ellipsis,
                                maxLines: 1,
                                style:  TextStyle(
                                  color: Colors.blueGrey,
                                  fontSize: 10.0,
                                  fontFamily: 'Poppins',
                              ),
                            ),
                          ],
                        )
                      ),
                      Container(
                        width: 25.0,
                        height: 20.0,
                        alignment: Alignment.center,
                        child:  Text(
                          "$index",
                          textAlign: TextAlign.center,
                          style:  TextStyle(
                            color: Colors.blueGrey,
                            fontSize: 10.0,
                            fontFamily: "Poppins"
                          ),
                        )
                      ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      },
    );
  }).toList(),
  height: 400,
  autoPlay: true,
)

如果你想在 Dart 代码中以持久的方式保存数据并能够在 Android 和 iOS 上使用它,我建议你使用 sqlite 插件 像那样:

https://github.com/tekartik/sqflite

否则如果你只需要保存一堆数据使用shared_preferences插件

https://github.com/flutter/plugins/tree/master/packages/shared_preferences

这两个插件都支持Android和iOS

您需要很多代码^_^(不是吗)。

因此,首先您需要通过 HTTP 调用获取您的 json。为此使用 http flutter package:

const request = "https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098";
http.Response response = await http.get(request);
debugPrint("Response: " + response.body);

用异步方法包装它:

void _jsonAndSqlite() async {
...
}

response 变量中你有 full JSON.

现在你需要序列化,我建议你这样做 really good reading

我为这个答案选择了 Manaul JSON Decoding

Manual JSON decoding refers to using the built-in JSON decoder in dart:convert. It involves passing the raw JSON string to the json.decode() method, and then looking up the values you need in the Map the method returns. It has no external dependencies or particular setup process, and it’s good for a quick proof of concept.

var myBigJSONObject = json.decode(response.body);
var status = myBigJSONObject['status'];
var totalResults = myBigJSONObject['totalResults'];
var myArticles = myBigJSONObject['articles'];

debugPrint("articles: " + myArticles.toString());

既然我们有文章会尝试通过Sqflite包将它们保存在Sqlite DB上

var myFirstArticle = myArticles[0];
var author = myFirstArticle['author'];
var title = myFirstArticle['title'];

// Get a location using getDatabasesPath
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'test.db');

// Delete the database
await deleteDatabase(path);

// open the database
Database database = await openDatabase(path, version: 1,
    onCreate: (Database db, int version) async {
  // When creating the db, create the table
  await db.execute(
      'CREATE TABLE Article (id INTEGER PRIMARY KEY, author TEXT, title TEXT)');
});

// Insert some records in a transaction
await database.transaction((txn) async {
  int id1 = await txn.rawInsert(
      'INSERT INTO Article(author, title) VALUES("$author", "$title")');
  debugPrint('inserted1: $id1');
});

就是这样!享受学习和编码的乐趣。阅读我为您发布的有关 JSON 序列化的文章并尝试使用我的代码,并尝试添加一些其他最佳实践,它们可能更适合您的需求。这只是一个快速的游乐场,好吧,玩。

所以我最终采用了这种方法:

[...]
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

 [...]
 void _jsonAndSqlite() async {
    const request =
        "https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098";
    http.Response response = await http.get(request);
    debugPrint("Response: " + response.body);

    var myBigJSONObject = json.decode(response.body);
    var status = myBigJSONObject['status'];
    var totalResults = myBigJSONObject['totalResults'];
    var myArticles = myBigJSONObject['articles'];

    debugPrint("articles: " + myArticles.toString());

    var myFirstArticle = myArticles[0];
    var author = myFirstArticle['author'];
    var title = myFirstArticle['title'];

    // Get a location using getDatabasesPath
    var databasesPath = await getDatabasesPath();
    String path = join(databasesPath, 'test.db');

    // Delete the database
    await deleteDatabase(path);

    // open the database
    Database database = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
      // When creating the db, create the table
      await db.execute(
          'CREATE TABLE Article (id INTEGER PRIMARY KEY, author TEXT, title TEXT)');
    });

    // Insert some records in a transaction
    await database.transaction((txn) async {
      int id1 = await txn.rawInsert(
          'INSERT INTO Article(author, title) VALUES("$author", "$title")');
      debugPrint('inserted1: $id1');
    });
  }