Django Rest Api And Flutter Dio Http Using Bloc Pattern

Working with Django Rest Framework is quite easy and simple. DRF has good follow along documents to create an API for your application. What we are not doing in this tutorial: * Not showing you how to create a Django Api. Not Showing you how to start a flutter application. What we are doing: * How to connect your Django rest framework Api to a flutter application. * Read and display data from your api on your flutter app, using Dio and Provider, both of which are Flutter packages.

Step 1. To start:

Make sure you have your api running. Your should have similar results as below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//json
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "http://localhost:8000/api/blogs/detail/72ab6429",
            "blog_id": "72ab6429",
            "blog_title": "Use the form below to post your blog.",
            "blog_type": "Sports",
            "blog_description": "Use the form below to post your blog.",
            "blog": "<p><span style=\"color: rgb(0, 0, 0); font-family: Poppins, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Use the form below to post your blog.</span></p>",
            "blog_image": null,
            "user": {
                "username": "OjObasi",
            }
        }
    ]
}

Now that you are sure you have your api, let's head back to our flutter app. Step 1. Edit pubspecs.yaml

1
2
3
provider: ^4.0.4
rxdart: ^0.23.1
dio: ^3.0.9

All these three are flutter packages. As their documentation says:

provider:

A mixture between dependency injection (DI) and state management, built with widgets for widgets.

rxdart:

RxDart adds additional capabilities to Dart  Streams  and StreamControllers

dio:

A powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc. Step 2. Model: let's create a model for our blog to match data from our api, and make it flutter friendly. This is useful as we won't need everything that is given from our api, we can map just to the fields we need.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//dart
// blog.model.dart
class BlogResultModel {
  final String error;
  int count;
  String next;
  dynamic previous;
  List<Blog> results;

  BlogResultModel({
    this.count,
    this.next,
    this.previous,
    this.results,
    this.error
  });

  factory BlogResultModel.fromJson(Map<String, dynamic> json) {
    return BlogResultModel(
      count: json['count'],
      next: json['next'],
      previous: json['previous'],
      results: _parseResult(json['results']),
      error: ""
    );
  }

  BlogResultModel.apiError(String error)
      : results = List<Blog>(), error = error;
}

_parseResult(List<dynamic> data) {
  List<Blog> results = new List<Blog>();
  data.forEach((item) {
    results.add(Blog.fromJson(item));
  });
  return results;
}
class Blog {
  String  blogid;
  String  blogtitle;
  String  blogdescription;
  String  blogtype;
  String  blog;
  String  blogimage;
  Map user;
  Blog(
      {this.blogid,
      this.blogtitle,
      this.blogdescription,
      this.blogtype,
      this.blog,
      this.blogimage,
      this.user,
      });

  factory Blog.fromJson(Map<String, dynamic> json) {
    return Blog(
      blogid: json['blog_id'],
      blogtitle: json['blog_title'],
      blogdescription: json['blog_description'],
      blogtype: json['blog_type'],
      blog: json['blog'],
      blogimage: json['blog_image'],
      user: json['user'],
    );
  }

 // Just incase, a blog has no image, lets return some placeholder image
  String getblogimage(){
    if(this.blogimage != null){
      return this.blogimage;
    } else {
      return 'http://localhost:800/static/img/no_image.png';
    }
  }
}

Step 3. Reading data from our api using dio.

I chose dio because of all the promises of the potential it offers. With dio, you won't have to worry about things like decoding the data returned from our api.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//dart
//blog.api.dart
class Api{  
 final String apiurl = 'http://localhost:8000/api/blogs/';

 Future<BlogResultModel> getBlogs() async {
    try {
      Response response =
          await _dio.get(apiurl);
      return BlogResultModel.fromJson(response.data);
    } catch (error, stacktrace) {
      return BlogResultModel.apiError("$error");
    }
  }
}

Step 4. The Bloc pattern with Rxdart and provider:

Interested in learning more about bloc with flutter(business logic component), there are tons of tutorials online on that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//dart
//blog.bloc.dart
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:rxdart/rxdart.dart';
import './blog.api.dart';
import './blog.model.dart';
class BlogBloc {
  ReplaySubject<BlogResultModel> _subject = ReplaySubject();
  Stream<BlogResultModel> get stream => _subject.stream;

  BlogBloc.empty();

  BlogBloc() {
       _subject.addStream(Stream.fromFuture(getBlogs()));
  }

  void dispose() async{
    _subject.close();
  }
}

final bloc = BlogBloc();
</pre><p><strong>Step 4. Add our blog stream as provider in the main.dart</strong></p><pre class="ql-syntax" spellcheck="false">//dart
//main.dart
child: MultiProvider(
        providers: [
          Provider<BlogBloc>(create: (context) => BlogBloc(), dispose: (context, bloc) => bloc.dispose()),
        ],
        ....

Step 5 and final. Usage in our widget

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//dart
// blogs.screen.dart
import 'package:provider/provider.dart';
...
class BlogsScreen extends StatefulWidget {
  const BlogsScreen();

  @override
  _BlogsScreenState createState() => _BlogsScreenState();
}

class _BlogsScreenState extends State<BlogsScreen> {
@override
  Widget build(BuildContext context) {
    final bloc = Provider.of<BlogResultModel>(context);
    return Scaffold(
       ...
        body: new StreamBuilder<BlogResultModel>(
                  stream: bloc.stream,
                  builder: (BuildContext context, AsyncSnapshot<BlogResultModel> snap) {
                    if (snap.hasError) {
                      return Center(
                          child: Container(child: Text('Something went wrong!')));
                    }

                    switch (snap.connectionState) {
                      case ConnectionState.waiting:
                        return new Center(
                            child: new CircularProgressIndicator(
                          backgroundColor: Colors.red,
                          valueColor:
                              AlwaysStoppedAnimation(...),
                        ));
                      default:
                         if (snap.data.error != null &&
                            snap.data.error.length > 0) {
                               return Center(
                              child: Container(
                                  child: Text('Something went wrong API!')));
                           }
                      List<Blog> blogs = snap.data.results;
                      if (blogs.length > 0) {
                        return ListView.separated(
                            shrinkWrap: true,
                            physics: const NeverScrollableScrollPhysics(),
                            itemCount: comments.length,
                            padding: new EdgeInsets.all(6.0),
                            itemBuilder: (BuildContext context, int index) {
                            Blog blog = blogs[index];
                             return Text(blog.blogtitle)
                           }
                       )
                      } else {
                        return Center(
                            child: Container(child: Text('No Blogs!')));
                      }
                  }
                }),

          );
        }
      }

Related Posts

0 Comments

12345

    00