Mobile Apps for the Supply Chain

Mobile Apps for the Supply Chain

Don’t Limit Yourself

While computing in the supply chain is nothing new, many of the existing systems and platforms are typically running on bulky and dated equipment, limiting the flexibility and efficiency of employees. This issue isn’t confined to one segment of the supply chain. Everyone from suppliers to retailers is often conducting day-to-day operations with outdated supply chain applications and hardware. With the ubiquity of mobile technology, introducing apps into the supply chain can be beneficial and seamless. Mobile supply chain apps can lead to improvements at every step, often in similar ways across segments. For example, inventory management apps allow suppliers to track their raw materials, manufacturers to decrease the time to manufacture and ship, and retailers to more effectively track their stock.

Companies frequently focus the majority of their mobile technology investment on consumer-facing apps, often at the expense of mobilizing their supply chain operations. Shockoe has partnered with several big box retailers, as their supply chain app developer, to create apps that support supply chain workforce and processes. The infographic, below, outlines examples of how these supply chain apps can support desired customer experiences by improving processes such as inventory management, production planning, material management, and resource planning processes, to name a few.

See The Potential

As a supply chain app developer, we work with our clients to help them gain insight into points along the supply chain that will benefit from mobile app investment in order to get customers their product faster, more efficiently, and more effectively. This leads to a better customer experience, which maximizes their existing investment in consumer-facing solutions and increases customer loyalty.

How Suppliers Benefit:

  • Accurate sourcing: track and distribute raw materials anytime, anywhere. Mobile apps connect manufacturers to the first step of the supply chain, the raw materials supplier.
  • Easier inventory control: inventory management with the tap of a finger. Warehouse management apps give you 24/7 control of inventory
  • Simplified logistics: order fulfillment that’s simple, intuitive, and on the go. Track the transit of materials from anywhere in the field.

How Manufacturers Benefit:

  • Faster production times: supply chain apps integrate with legacy systems to cut down on the overall production process, decreasing the time to manufacture and ship.
  • Recall/damage control: simplify field assessments and integrate data captured in the field from mobile apps with back office applications. The faster a customer complaint is resolved, the more likely customers are to become repeat customers.
  • Better final product: tighter quality control means better oversight. All of this amounts to a higher quality product.

How Distributors Benefit:

  • Improved warehouse management
  • Smarter communication
  • Increased “on-time” delivery

How Retailers Benefit:

  • Better stock management
  • More efficient front-line employees
  • Less shrinkage

How Consumers Benefit:

  • Faster shipments
  • Accurate order tracking
  • Great customer experience

Increase Your Bottom Line

Partnering with an experienced supply chain app developer to digitize the supply chain means organizations can have the best of both worlds by increasing efficiency to decrease costs and making sure customers become ambassadors for the brand, leading to repeat business and long-term revenue growth.

 

Apps for the Supply Chain

 

Asset/Inventory Management Apps in Record Time with Flutter

Asset/Inventory Management Apps in Record Time with Flutter

Shockoe specializes in utilizing tools which can most efficiently provide a beautiful experience for a given project. We have a history with cross-platform frameworks, as they can often quicken the development period for a mobile app considerably. Many of our projects were historically built on Titanium, and a few more recently were undertaken with React-Native.

When Flutter was announced, we knew we had to keep a close eye on it, and we were eager for it to reach the point where it was mature enough to build a robust production app. The results were astounding. Not only does it ease many of development pains present in other cross-platform frameworks, it also gives you beautiful UI out of the box, and extraordinary speed as it is blazingly fast to develop. In fact, the entirety of the development you will see later in this post was completed single-handedly in a matter of hours!

Flutter for Inventory Management Apps

At Shockoe, we have a point of creating great inventory/asset apps the help manage resources, assets, and inventory at a number of large-scale companies. A few reasons why Flutter has been a particularly great fit for developing these kinds of apps include:

  • List Convenience: Turning a raw data list of assets and inventory into an actual list laid out on screen couldn’t be easier. It can be accomplished in a handful of lines of code.
  • Beautiful by Default: Apps in this category have a heavy focus on functionality. A framework which looks good in its most basic state lets you focus on the utility and devote as much time as you decide to enhance UI and delivering the right content.
  • List Performance: Flutter renders every pixel on the screen itself, allowing for a performance unparalleled by other frameworks. It touts its ability to maintain 60fps, and scrolling is buttery-smooth even on massive lists.
  • Empty/Loading State Simplicity: Most screens will be heavily data-driven. In some environments, displaying states like waiting for an API response or failing to connect can become extremely cumbersome. Flutter makes it easy to build a UI which reacts to these in-between moments gracefully.
  • Object-Oriented: Unlike Titanium and React-Native, which use Javascript, Flutter apps are written in Dart. This offers a number of benefits, like the lessened runtime error rate of strongly typed languages. The reason why it is perfect for this case is that Object-Oriented design allows for easy 1:1 mappings between real objects and their representations in code. Are you a retailer which specializes in shoes? Well, chances are your app is going to have a class Shoe and an instance of it is going to tell you everything you need to know about that specific shoe.

In this post, we’re going to take a look at building an inventory management app and not just the Flutter bit. This post includes a fully functioning Node.js backend as well — ensuring you successfully deliver your message and content to your users.

Note: This will not be a step-by-step guide, as that would be difficult to digest at a high level. Instead, we will look at each piece and break down the important components.

Inventory App Features We’ll be Building

Below is everything entailed in going from an unstarted project to a functioning application pulling real data. Here is what we will end up with:

inventory app example pulling real data

Let’s dive in!

Basis

We will use the example of a library — yes, the variety filled with a book! A library is essentially a warehouse filled with inventory (in this case, books). For many businesses an inventory application, at its core, would support browsing and tracking items. In the context of a library, those functionalities manifest themselves in the following ways:

  • Browsing
    • view the full catalog
    • search for a specific title
    • view information about a specific title
  • Tracking
    • see a title’s availability
    • check out a copy
    • return a copy

Our app will handle all of the above.

Setup

Database
MongoDB will be used to store the data. There is no special setup required, we just load all the items into a collection and later run the Node.js server on the same machine to leverage Mongo’s already exposed localhost connection.

Most likely, when building an app of this type, it will be used to access an existing dataset. The data in this example will be a subset of the most popular titles on Project Gutenberg supplemented with Wikipedia details.

Backend (API)
For our server, we will be using hapi with a few smaller dependencies like the official Node.js MongoDB drive and boom for error handling.

Once hapi is installed, we must create our startup file. This will get the server up and running to fulfill requests. Let’s use index.js.

'use strict';

const Hapi = require('hapi');
const routes = require('./routes');

const server = Hapi.server({
port: 3000
});

server.route(routes.allRoutes);

const init = async () => {

await server.start();
console.log(`Server running at: ${server.info.uri}`);
};

process.on('unhandledRejection', (err) => {

console.log(err);
process.exit(1);
});

init();

Tiny, right? Hapi requires very little boilerplate. The majority of this is ripped right from hapi’s Getting Started guide. Besides removing the host property on the server configuration object in order to fall-back to the machines hostname, the only custom line is as follows:

server.route(routes.allRoutes);

 

This line imports and registers all of the endpoints we define in our second, and final, file: routes.js. We separate these so that the server configuration doesn’t get drowned out by the much larger endpoint definitions. In a more complex app, we would likely want multiple files which logically group endpoints into smaller buckets. Here is routes.js with an example endpoint. Its only job is to export an array of configuration objects.

const Boom = require('boom');

exports.allRoutes = [
  {
    method: 'GET',
    path: '/',
    handler: async (request, h) => {
      return 'Hello world';
    }
  }
];

 

Flutter
Enter Flutter! When creating a new Flutter project through IntelliJ, a main.dart file is created for a basic sample app which implements a counter. This is helpful when learning, but we need to rip out some of that starter code. Here is a single page app which we can use as a starting point.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Library',
      theme: ThemeData(
        primarySwatch: Colors.deepOrange,
      ),
      home: CatalogPage(),
    );
  }
}

class CatalogPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Catalog'),
      ),
      body: Center(
        child: Text('List of books'),
      ),
    );
  }
}

 

flutterSetup

 

 

 

 

 

 

 

 

 

 

 

 

 

Catalog

Backend
Now that we’re set up, let’s start serving up data. We replace the example endpoint we defined before with one which returns the full list of books in the database.

const MongoClient = require('mongodb').MongoClient;
const Boom = require('boom');

exports.allRoutes = [
  {
    method: 'GET',
    path: '/bookList',
    handler: async (request, h) => {
      let client;
      try {
        client = await MongoClient.connect('mongodb://localhost:27017');
        let books = client.db('inventory').collection('books');

        // fetch all books
        return await books.find({},
          {
            projection : {
              _id: 0,
              id: 1,
              title: 1,
              authors : 1,
            }
          }
        ).toArray();
      } catch (e) {
        console.error(e.message);
        return Boom.internal(e);
      } finally {
        if (client && client.close){
          client.close();
        }
      }
    }
  }
];

 

You may notice async/await syntax. As it is available in both recent Node.js versions and Dart, we will use it throughout the backend and the app.

There isn’t too much going on here. We connect to MongoDB, specifically the collection books in the database inventory, and run a find query with an empty filter object (first argument) so that all records are pulled. For cleanliness of data, we project only the properties of a book which we would be interested in when listing them en masse.

Flutter
The first thing we need to define is the representation of a book. We will go ahead and include all fields we need to be known for a book, even though only a few of them will be populated from the results of the /bookList endpoint.

class Book {
  final String id;
  final String title;
  final List<String> authors;
  final String releaseDate;
  final String description;
  final int totalCopies;
  final int availableCopies;

  /// Creates a Book instance out of JSON received from the API.
  Book.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        title = json['title'],
        releaseDate = json['releaseDate'],
        description = json['description'],
        totalCopies = json['totalCopies'],
        availableCopies = json['availableCopies'],
        authors = json['authors'].retype<String>();
}

 

We will use the “Serializing JSON inside model classes” strategy shown in Flutter’s JSON and serialization guide.

CatalogPage is a Stateless widget, because the full screen including the appbar doesn’t need to be re-rendered in the future, just the content. For that, we create a Stateful Widget, called CatalogList, which we will place in the body of CatalogPage. To keep this example concise, network requests will be made directly from widgets. It is better to practice to split them out into a non-UI library. Here is Catalog with basic display functionality complete followed by a breakdown below.

/// The list of books.
class CatalogList extends StatefulWidget {
  @override
  _CatalogListState createState() => _CatalogListState();
}

class _CatalogListState extends State<CatalogList> {
  /// All books in the catalog.
  List<Book> books;

  /// Books currently being displayed in the list.
  List<Book> displayedBooks;

  /// Kicks off API fetch on creation.
  _CatalogListState() {
    _fetchBookList();
  }

  /// Fetches the list of books and updates state.
  void _fetchBookList() async {
    http.Response response = await http.get('http://<API location>/bookList');
    List<Map<String, dynamic>> newBooksRaw =
        json.decode(response.body).retype<Map<String, dynamic>>();
    List<Book> newBooks =
        newBooksRaw.map((bookData) => Book.fromJson(bookData)).toList();
    setState(() {
      books = newBooks;
      displayedBooks = books;
    });
  }

  @override
  Widget build(BuildContext context) {
    return displayedBooks != null
        ? Column(
            children: <Widget>[
              new Expanded(
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: ListView.builder(
                    itemBuilder: (BuildContext context, int index) => Card(
                          elevation: 2.0,
                          child: ListTile(
                            title: Text(
                              displayedBooks[index].title,
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                            ),
                            subtitle:
                                Text(displayedBooks[index].authors.join(' | ')),
                          ),
                        ),
                    itemCount: displayedBooks.length,
                  ),
                ),
              ),
            ],
          )
        : Center(child: CircularProgressIndicator());
  }
}

 

basicCatalog

 

 

 

 

 

 

 

 

 

 

 

 

 

Fetch Data
When the CatalogList widget is created, we immediately want to fetch the data on all books from the backend. We go ahead and create two list references, one for all the data downloaded and one for data currently displayed, as we know search functionality is coming and we won’t always be displaying the full catalog on screen. When data is first downloaded, though, these will be the same. We take advantage of the fromJSON serialization constructor we created to convert the backend’s JSON response into a list of formed Book objects in one list mapping call.

/// All books in the catalog.
List<Book> books;

/// Books currently being displayed in the list.
List<Book> displayedBooks;

/// Kicks off API fetch on creation.
_CatalogListState() {
  _fetchBookList();
}

/// Fetches the list of books and updates state.
void _fetchBookList() async {
  http.Response response = await http.get('http://<API location>/bookList');
  List<Map<String, dynamic>> newBooksRaw =
      json.decode(response.body).retype<Map<String, dynamic>>();
  List<Book> newBooks =
      newBooksRaw.map((bookData) => Book.fromJson(bookData)).toList();
  setState(() {
    books = newBooks;
    displayedBooks = books;
  });
}

 

Try/catch around the async body of _fetchBookList is omitted for readability. Make sure to catch possible exceptions/errors in production.

Build a List
Here is where we see Flutter start to shine. To convert this list of Book data into a rendered list on the screen, all we have to do is write an itemBuilder function which returns what a given item in the list will look like, then pass in the list of data and it’s length. We use a Material Design Card containing a ListTile- a prebuilt widget which displays a title and subtitle (and optionally additional inner widgets).

child: ListView.builder(
  itemBuilder: (BuildContext context, int index) => Card(
        elevation: 2.0,
        child: ListTile(
          title: Text(
            displayedBooks[index].title,
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
          subtitle:
              Text(displayedBooks[index].authors.join(' | ')),
        ),
      ),
  itemCount: displayedBooks.length,
),

 

styledListItems

 

 

 

 

 

 

 

That’s all it takes to build a ListView which is ready for production. It will lazily render new rows as they are scrolled into view, gracefully handle changes to the list of Books, adapt scrolling behavior to OS, and perform fantastically.

Much of the above is styling as well. We could get something functional in half as many lines.

child: ListView.builder(
  itemBuilder: (BuildContext context, int index) => ListTile(
        title: Text(displayedBooks[index].title),
        subtitle:
            Text(displayedBooks[index].authors.join(' | ')),
      ),
  itemCount: displayedBooks.length,
),

 

unstyledListItems

 

 

 

 

 

Handle Loading State
When the Catalog List is created, the data list which our ListView is populated from, displayedBooks, is null.

List<Book> displayedBooks;

 

Once the data fetch is complete, that variable will point to a valid List.

setState(() {
  books = newBooks;
  displayedBooks = books;
});

 

Once this occurs, the ListView can start rendering rows. In the meantime, we need to render something different. If handling moments like these require a lot of development effort, it can feel counterproductive to tackle them right out of the gate while true functionality is still being worked out. This can lead to polish/UX tasks being put on the afterburner. With Flutter, it’s easy to handle loading during our first pass at the screen. We just use a ternary expression in the build function to describe an alternate visual while displayedBooks is still null.

@override
Widget build(BuildContext context) {
  return displayedBooks != null
      ? Column(
          // ...rest of widget hierarchy for loaded state
        )
      : Center(child: CircularProgressIndicator());
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Search
Supporting search requires two main changes.

  1. Add a search input field which fires an event when it changes
  2. Add a search function to filter the displayed list when the search event is fired

An input field which fires an event is achieved with the combination of TextField and TextEditingController

/// The controller to keep track of search field content and changes.
final TextEditingController searchController = TextEditingController();
child: TextField(
  decoration: InputDecoration(hintText: 'Search for titles...'),
  controller: searchController,
),

 

With the search bar in place, we can register a listener when the CatalogList is created to fire the function which will filter the full book list down to results to be displayed and update state. If the search text becomes empty, the list is set back to the full catalog.

/// Kicks off API fetch on creation.
_CatalogListState() {
  _fetchBookList();
  searchController.addListener(_search);
}
/// Performs a case insensitive search.
void _search() {
  if (searchController.text == '') {
    setState(() {
      displayedBooks = books;
    });
  } else {
    List<Book> filteredBooks = books
        .where((book) => book.title
            .toLowerCase()
            .contains(searchController.text.toLowerCase()))
        .toList();
    setState(() {
      displayedBooks = filteredBooks;
    });
  }
}

 

 

Navigation
The Catalog page is complete, and now we need to be able to take a deeper look at an individual item. The next page will be called DetailPage, so we’ll rig up each item in the list to move forward to the respective book’s details. Conveniently, ListTile has builtin touch handling, so we can just add a single property

onTap: () {
  Navigator.of(context).push(MaterialPageRoute(
      builder: (BuildContext context) {
    return DetailPage(displayedBooks[index].id);
  }));
}),

 

We only pass the id instead of the Book instance, since we will be fetching the book’s most up-to-date full data from the backend when loading DetailPage anyways.

Details

Backend
We add a new endpoint configuration object onto allRoutes to return full details of a title. It is nearly identical to the full listing except we switch to findOne, add a filter for an id passed from the app, and project additional fields. We also call out to a function to calculate the number of copies available, but we will wait to see that in the Tracking section.

{
  method: 'GET',
  path: '/book',
  handler: async (request, h) => {
    let client;
    try {
      client = await MongoClient.connect('mongodb://localhost:27017');
      let books = client.db('inventory').collection('books');

      // fetch matching book
      let book = await books.findOne(
        {
          id: request.query.id
        },
        {
          projection : {
            _id: 0,
            id: 1,
            title: 1,
            authors : 1,
            releaseDate : 1,
            description: 1,
            totalCopies: 1,
            checkedOutTo: 1
          }
        }
      );
      setAvailableCopies(book);
      return book;
    } catch (e) {
      console.error(e.message);
      return Boom.internal(e);
    } finally {
      if (client && client.close){
        client.close();
      }
    }
  }
},

 

 

Flutter
The basic DetailPage widget is a bit larger, but most of the functionality will look familiar from CatalogList. This time we make DetailPage itself the Stateful Widget, since we want to allow the AppBar to update with the book’s title once data is loaded.

/// The screen which displays the full details of a given book.
class DetailPage extends StatefulWidget {
  final String bookId;

  DetailPage(this.bookId);

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

class _DetailPageState extends State<DetailPage> {
  /// The full book data.
  Book book;

  /// Kicks off API fetch on creation.
  _DetailPageState() {
    _fetchBookDetails();
  }

  /// Fetches the books details and updates state.
  void _fetchBookDetails() async {
    http.Response response =
        await http.get('http://<API location>/book?id=${widget.bookId}');
    Map<String, dynamic> newBookRaw = json.decode(response.body);
    Book newBook = Book.fromJson(newBookRaw);
    setState(() {
      book = newBook;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book?.title ?? ''),
      ),
      body: book != null
          ? new Center(
              child: new SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: Card(
                    elevation: 5.0,
                    child: Center(
                        child: Padding(
                      padding: const EdgeInsets.fromLTRB(24.0, 0.0, 24.0, 24.0),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          _BodySection('Author', book.authors.join('\n')),
                          _BodySection(
                              'Release Data', book.releaseDate ?? 'N/A'),
                          _BodySection('Description', book.description),
                        ],
                      ),
                    )),
                  ),
                ),
              ),
            )
          : Center(child: CircularProgressIndicator()),
    );
  }
}

class _BodySection extends StatelessWidget {
  final String title;
  final String content;

  _BodySection(this.title, this.content);

  @override
  Widget build(BuildContext context) {
    return new Padding(
      padding: const EdgeInsets.only(top: 24.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Text(title, style: Theme.of(context).textTheme.title),
          Text(content, style: TextStyle(color: Colors.grey[700]))
        ],
      ),
    );
  }
}

 

We could have fetched all these details back on the CatalogPage and simply passed them through the be displayed on the DetailPage. The reason we opt for pulling the data fresh is to ensure we have the very latest details on an item when we view it. In this example, it’s unlikely that any of the fields we are displaying would change frequently. In other industries, however, a DetailPage might be displaying volatile data. These considerations especially come into play when we add our next section.

Tracking

We will support two operations on books: checking out and returning. These operations will drive the number of copies available to be checked out by other users as well. When a user checks a book out, they are grabbing it from the library and marking one of the copies as taken by them. When they return a copy, they are again providing their name and stating that they have placed the copy back into the library. In this example, no unique identifiers are used for individual copies, however, it would be a small change to add an additional field to the app and modify the backend to accept a copy identifier (potentially a barcode).

The three fields in the database driving these interactions will be:

  • totalCopies – total number of copies of a title which the library has in circulation
  • checkedOutTo – array of names to which copies are checked out
    along with one virtual field calculated at request time
  • availableCopies – number of copies in the library available for checkout (total copies – checked out copies)

 

Backend

We add two endpoint configuration objects. /checkOutBook will push a name onto the checkedOutTo array, and /returnBook will splice a name out of it.

{
  method: 'POST',
  path: '/checkOutBook',
  handler: async (request, h) => {
    let client;
    try {
      client = await MongoClient.connect('mongodb://localhost:27017');
      let books = client.db('inventory').collection('books');

      // update book data with name added to check out list
      let bookResult = await books.findOneAndUpdate(
        {
          id: request.payload.id
        },
        {
          $push : { checkedOutTo : request.payload.name }
        },
        {
          projection : {
            _id: 0,
            id: 1,
            title: 1,
            authors : 1,
            releaseDate : 1,
            description: 1,
            totalCopies: 1,
            checkedOutTo: 1
          },
          returnOriginal : false
        }
      );
      let book = bookResult.value;
      setAvailableCopies(book);
      return book;
    } catch (e) {
      console.error(e.message);
      return Boom.internal(e);
    } finally {
      if (client && client.close){
        client.close();
      }
    }
  }
},
{
  method: 'POST',
  path: '/returnBook',
  handler: async (request, h) => {
    let client;
    try {
      client = await MongoClient.connect('mongodb://localhost:27017');
      let books = client.db('inventory').collection('books');

      // get current list of checked out copies
      let currentBookData = await books.findOne(
        {
          id: request.payload.id
        }
      );
      let nameIndex = currentBookData.checkedOutTo.indexOf(request.payload.name);
      if (nameIndex !== -1){
        currentBookData.checkedOutTo.splice(nameIndex, 1);
      }

      // update book data with name removed from check out list
      let bookResult = await books.findOneAndUpdate(
        {
          id: request.payload.id
        },
        {
          $set : { checkedOutTo : currentBookData.checkedOutTo }
        },
        {
          projection : {
            _id: 0,
            id: 1,
            title: 1,
            authors : 1,
            releaseDate : 1,
            description: 1,
            totalCopies: 1,
            checkedOutTo: 1
          },
          returnOriginal : false
        }
      );
      let book = bookResult.value;
      setAvailableCopies(book);
      return book;
    } catch (e) {
      console.error(e.message);
      return Boom.internal(e);
    } finally {
      if (client && client.close){
        client.close();
      }
    }
  }
}

 

We also add a quick helper method to routes.js to calculate available copies.

function setAvailableCopies(book){
  book.availableCopies = book.totalCopies - (book.checkedOutTo ? book.checkedOutTo.length : 0);
}

 

 

Flutter
Beneath the other body sections, we add availability details and a small form for checkout/return. Since both actions require the same info, they can share an input box.

_BodySection('Available Copies',
    '${book.availableCopies} / ${book.totalCopies}'),
Column(
  children: <Widget>[
    TextField(
      decoration:
          InputDecoration(hintText: 'Enter name'),
      controller: nameController,
    ),
    new Padding(
      padding: const EdgeInsets.only(top: 16.0),
      child: new Row(
        mainAxisAlignment:
            MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          RaisedButton(
            child: Text('Check Out!'),
            onPressed: fieldHasContent &&
                    book.availableCopies > 0
                ? _checkOut
                : null,
          ),
          RaisedButton(
            child: Text('Return'),
            onPressed:
                fieldHasContent ? _return : null,
          ),
        ],
      ),
    )
  ],
)

 

 

detailsDisableddetailsEnabled

 

These buttons call functions to pass the current book id and entered a name to the backend, and will update state with the new version of book details returned. This ensures that the available copy count will stay in sync with actions performed.

Each button is conditionally disabled by passing null to its onPressed property. Both buttons are disabled by the fieldHasContent flag which is set when the name input field is empty, and the check out button is additionally disabled when there are no available copies.

We have a functional, performant, aesthetically pleasing app with associated backend all in ~500 lines of code. Creating a solution for Asset/Inventory Management has never been easier, and Flutter continues to improve daily. Don’t let it’s “beta” tag fool you, Flutter is production ready. In fact, we just built a production Inventory Management application for Belden Brick, so that the brick distributors they work with can access browse their inventory, search products, view product images, order samples, and more!

Below you will find the full files from this demo.

Flutter: https://bitbucket.org/snippets/shockoe/7eMxEq
Hapi: https://bitbucket.org/snippets/shockoe/ne8jR6

 

 

Related post:

Three Reasons FLutter is a Viable Cross-Platform Framework
Google Flutter goes Beta at #MWC18

Design Tips to Increase Satisfaction in Banking Apps – Part 1 of 2

Design Tips to Increase Satisfaction in Banking Apps – Part 1 of 2

Retail banking consumers now prefer using their mobile devices more than any other bank interaction, which makes a mobile app a primary component of overall customer satisfaction. With greater ease switching banking providers at a moment of dissatisfaction, banks need to place extra emphasis on keeping their customers happy and loyal. This starts by giving customers the best tools available and a user experience that helps them access and navigate their banking needs without difficulty. Read more about our design tips for banking apps below. 

 

For the first section of this two-part series, we will cover examples of best practices that we have seen play a role in facilitating engagement and improving the user experience.

Any questions surfacing as you read? Give us a ring! You can always connect with us here.

 

Search & Navigation Part 1

Content Part 1

Guidance Part 2, coming soon

Privacy & Security Part 2, coming soon

Appearance Part 2, coming soon

 

 

Search & Navigation

search-navigation-components-of-app-satisfaction

According to J.D. Power, ‘Ease of Navigating’ is the key differentiator among top-performing mobile banking apps. If a consumer can find what they need in the app, this often yields a happy customer. This satisfaction can also impact bank operations by reducing calls to support centers with potentially aggravating wait times.

 

Let’s jump head first into some easily-executed ideas to help improve your app’s search & navigation as early as today.

 

Easy Login

 

Biometric logins such as fingerprint, face, or voice can facilitate a client’s access to their account.

easy-login-biometric-one-touch

Personalization Capabilities

 

Some banks give the user the ability to customize their application experience to their needs making each visit one that addresses their specific needs.

personalization-personalize

Using Navigation Icons with Label

 

An icon is meant to be universally recognized, but in many cases, they are not. It’s always a safe bet to provide a label next to the icon to provide clarity.

 

using-navigation-icons-with-labels

Use Plain & Simple English

 

Avoid using branded names that might be intuitive to your company, but not to a user. In short: use plain English when possible.

 

use-plain-simple-english-branded-names

 

Transaction History Search

 

Most banking apps default to filtering transaction history by date. Giving the user the ability to search their account is one more way to facilitate finding that specific transaction they have in mind.

 

transaction-history-search

Appwide Search

 

Few banks offer app-wide search to locate features & information. It might just be what your clients needed to discover new or undiscovered features.

 

appwide-search-my-bank

Clear ‘Back’ Access

 

Avoid using a home icon or cancel in place of a back.

 

clear-back-access-button

Autofill/Type-Ahead Searching

 

We continue to be surprised at the number of banks not make use of this simple yet effective interaction. Your customers will be thrilled to have it implemented.

autofill-type-ahead-searching

Content

 

The content that users access in-app should be concise, easy to find, easy understand, and help them reach their goals—simple right? Here are a few ideas:

 

Key Information Front and Center

 

Some applications give users the choice to view account their account balances before login.

 

key-information-front-center

 

Helpful Services

 

Provide customers with additional services that could help them reach their financial goals.

 

helpful-services

 

Real-Time Alerts

 

Use real-time alerts to keep customers informed on important account updates such as direct deposits, personal information changes, and bill due dates.

 

real-time-alerts

 

Avoid Hiding Information

 

Some banks hide interest rates behind an extra tap or elaborate application process. Be nice to customers and let them know what they need to know.

 

avoid-hiding-information

 

Avoid Jargon-Heavy Content

 

Avoid words such as Debit, Payee, APR — instead use Withdrawal, Recipient, Interest Rate.

 

avoid-jargon-heavy-content

Guidance Part 2, coming soon

Privacy & Security Part 2, coming soon

Appearance Part 2, coming soon

 

Editor’s note: 

We know you’re thirsty for more. Part 2 will be coming very soon! While you wait, check out our latest thoughts on UX Strategy for Banks. 

Have any additional questions or want to discuss what Shockoe can do for you? Click here to connect with us. 

Adjustments to Your Bank’s UX Strategy

Adjustments to Your Bank’s UX Strategy

Among the sea of social media apps, news apps, and photo book-making apps I use – I have three kids! –  is my mobile banking app. I bank at a “traditional” or “retail” bank, meaning it has branches, versus an online-only bank. That being said, I never go to a branch. Anything I need to do I can do using my mobile banking app: check my transactions, transfer money between accounts, or deposit a check. Believe it or not, these things that users have come to expect out of their mobile banking experience, I have had to figure out rather the hard way with my current mobile banking app. The user experience of my bank’s app has never been truly intuitive, though it has gone through multiple iterations. Banking apps should not make it difficult for customers to complete basic tasks. By continuously putting user experience first and applying the following adjustments to your UX Strategy, your bank is guaranteed to drive revenue through customer loyalty. 

 

The first time I used Venmo, an app designed solely for people to be able to electronically send money, I immediately noticed the intuitiveness of the app. A few months after I started using Venmo, my bank came out with an identical feature. I could send money to friends or family no matter who they banked with. That’s as much as I know about it because the idea of using my bank’s clunky app for a task I found myself doing frequently seemed overwhelming, so I stuck with Venmo.

 

As more FinTech companies continue to disrupt, develop and innovate mobile banking applications, it will occur at the expense of lost market share for traditional banking institutions.The rising FinTech sector is making it easier making it easier for their customers to do more with their money.

 

At Shockoe we have advised our financial industry partners to consider two adjustments to their UX strategy as a result of this changing environment:

 

Implementing machine learning.

 

I, like many others, have predictable spending habits. I shop at the same places, I pay my mortgage, and I head to the grocery store at the same time. To keep an eye on my spending, I log into my banking app quite regularly.

 

The reason I point out these things is that this is all data that the banks can use to help make me a “stickier” client. I get random ads sometimes when I log into my account, but they don’t happen as I take an action, nor are they personalized to me.

 

Banks are leaving a great opportunity to interact with their customers on the table. They could ask questions about unusual spending to improve security and more importantly learn about shifting habits. e.g. “It looks like you made a purchase at Wegman’s last weekend, was that you?”, the app learns that this is now part of my purchase history and the algorithm changes. Similarly, new products could be touted as client data captures what looks like a night out: “Looks like you left the kids at home and recently went to the movies! Did you pay your babysitter with our easy system to send money electronically to people?”

 

There should always be a way to turn these kinds of alerts off, but banks know so much about their users, and using machine learning capabilities is one way they can use that data to try to engage more with their clients.

 

Making banking apps more social. 

 

A big part of Venmo’s popularity comes down to the fact that they’ve tapped into the special sauce of why social media is so popular/addictive. You can interact with people, keep up with their latest transactions and see why they’re sending or receiving money for. Obviously, security is n essential consideration in banking, but for people that are willing to share, this is another outlet for banks to engage their audience, encourage product use, and compete in an increasingly competitive FinTech industry.

 

Do people want to be able to brag about their savings account interest rate? What else are people comfortable with being able to show off in regards to their banking relationship? We work with our clients to run user group feedback sessions to find the answers to things like this. User feedback should be an essential consideration in designing an engaging user experience that extends beyond logging in and checking on account statements.

 

Banking apps should no longer think of themselves as a one dimension account statement viewing portal. FinTech will eventually edge them out of services such as peer to peer payments (venmo), machine learning (mint), and potentially edge them out of being a provider at all in lucrative services. I am a project manager at Shockoe and I’ve worked with two large banking clients as part of my tenure here, and these thoughts are coming from meetings with them and our approach helping them stay engaged with their user base and attract more users through their mobile app solutions. What’s cool is our clients know we work together to create mobile applications that people use, love, and remember, and that sometimes the problems are even solved by the project management team.

How to Choose the Right Software Development Firm

How to Choose the Right Software Development Firm

Thousands of companies have adopted enterprise mobile apps during a time when they were becoming popular and they wanted to “check the box” that they had an app. While most companies understand the benefits of mobile technology, unfortunately, some have failed to exercise due diligence when developing their app and selecting the proper vendor for their project. There are many great mobile development firms out there, but making sure you have a good fit for a particular project is the most important factor to consider when selecting a vendor. Let’s go through the checklist of items that should be considered any time your organization wants to develop an enterprise app.

1. Does the developer have previous experience with similar projects?

This is an important question to ask because the quality and agility of the project will be significantly better if the mobile dev team has completed a similar project before. For example, if you’re in the banking industry, look for a firm that has made banking apps before. They may have made other great apps for other industries, but that doesn’t always translate into a successful project in yours.

2. Does the vendor have a dedicated UI/UX team?

If the answer is no, immediately disqualify that vendor. Huge mistake companies have made was neglecting the end-user experience. The “it doesn’t matter if it looks that great because they have to use it anyway” line of thinking is counterintuitive with the premise of increasing employee work performance and satisfaction. A dedicated and experienced UI/UX team will make sure the app is intuitive. After all, a user interface is like a jokeif you have to explain it, then it’s not very good

3. Look at customer reviews.

Most mobile agencies, if they have been in the industry long enough, will have customer reviews. These can be found on sites like Clutch and GoodFirms and can provide great insight into how successful similar projects were and how easy it is to work with their team.

4. Get to know the team.

When selecting the right firm, get to know the team that will be assigned to your project. Typically a team will consist of a couple developers, a designer, and a project manager. It’s important to get to know these people to determine how easy it will be to work with them, and also to decipher if they are qualified to take on your project.

5. Look for someone who is concerned with the overall objective, not just the app.

Mobile agencies need to understand the “big picture” that the client wants to accomplish. Some developers may get caught up with the app development and making it really “sweet,” which is good, but clients don’t care about how cool the app is if it doesn’t address the business objectives it is supposed to help accomplish.

6. Don’t get hung up on price.

The saying that “you get what you pay for” has never been truer than in the mobile dev industry. A huge mistake that a lot of companies have made was selecting vendors who proposed the lowest prices. More often than not, those apps did not perform as desired and business goals were never realized. Make sure the mobile agency you select fits all the other criteria, and then discuss pricing.

7. Ask for a demo.

Even if the app is custom, always ask for a demo of an existing app that is similar to the project you want to be completed. This is the easiest way to determine the quality of the apps that could be developed for your company.

8. Ask a lot of questions.

This seems like a no-brainer, but failing to ask lots of questions was a common mistake made by companies that paid for apps during the initial mobile app frenzy. In an effort to save money by going with the cheapest vendor, these companies instead wasted money on apps that didn’t meet their needs. In the long run, they spent more on app development than they would have had they chosen the best agency for the job instead of the cheapest. Bring in all the internal stakeholders and come up with a list of questions to ask the vendor to make sure no details are left out, which could be detrimental to the project down the line.

9. Do they know your industry?

Mobile agencies that understand your industry and business are invaluable. Instead of just taking orders and accepting the requirements, look for a firm that can challenge your own ideas and provide insight that you may not have thought of before. If they are experienced, they should know plenty about how certain apps work within your business and be able to provide best practices for the project.

In conclusion, after taking everything on this list into consideration, you should be able to narrow down the mobile development firm that is right for your project. Even if your company has had a bad experience in the past, following these guidelines should help you avoid wasting money on an app that in the end does nothing to push the objectives of your business.

Ready to get to know the Shockoe team? Reach out to us and see if we’re a good fit for your next app development project. 

App Microtransactions: The Good, the Bad, and the Ugly

App Microtransactions: The Good, the Bad, and the Ugly

microtransactions-in-app-purchaseThe concept of app microtransactions is not foreign to consumers of mobile technology in the modern app market. Apps that include or require consumers to submit payment for additional features or add-ons have become the norm. This business model has grown organically as a result of users having a hard time deciding whether or not they should spend their hard-earned money on an app. By making an app free and offering the user the choice to pay for additional features as needed, the barrier to entry for new users to download and use an app has been greatly reduced. This model has its benefits, its downfalls, and its unsightly impact on consumers’ wallets.

 

The Good  

Developers have immensely benefitted from microtransactions. Developers can more easily test the waters of the market with their apps. Instead of overcoming the hurdle of figuring out whether or not their app is a good idea in the first place, they can create a slimmed-down version of it and release it as a free product. After it has been on the market as a free app for some time, developers can access the potential for the app to have continued success. If the app proves to be working, then they can take the time to implement the rest of the features and release those features at a cost to the current user base in a new update through a microtransaction.

Microtransactions have been most successful in the game app market. Games have an advantage over value product apps in that games are inherently more addictive and entice the user to want to win. Developers have a big opportunity here to add “pay to win” add-ons to help their users win by giving them in-game boosts. These boosts usually come at a small cost for small boosts, and can cost upwards of $100 for larger boost packs. This gives developers the opportunity to make more money than they would with the typical $1.99 to $9.99 price tag of most paid apps on the market.

 

The Bad

While this model benefits developers, it negatively impacts the quality of apps on the market by requiring a microtransaction to unlock more features. Apps are being released to the market that are not full featured and polished, leaving users with half-made apps and wanting more. While it may benefit developers to release apps on this model, it is unsatisfactory for consumers who look for apps to fit their needs but find that the apps lack the features they want.

It may be that you find a free app on the market that you really enjoy, but unless many others find the same enjoyment in the app as you, the developer might not release an update for it. You wouldn’t want to go out and buy a brand-new vacuum just to come home and find that the one you bought will require you to purchase two or three other attachments in order to vacuum in tight or high places when the packaging claimed it was compatible, right? Apps should not operate this way either, or they will continue to deter consumers from exploring and discovering new apps on the market outside of the mainstream apps that their friends and family use.

  

The Ugly

microtransactions-the-uglyAdditionally, apps that have success with this model are leaving users with the ugly truth that they will need to sink more and more money into an app to get the most out of it. This practice is particularly prevalent in game apps. As I mentioned earlier about developers having a good opportunity to make money, requiring microtransactions in order for the consumer to be successful in an app exploits the addictive nature of the game, and promises the user that by paying money, they will be able to win. Oftentimes these games involve high scores, and consumers will do anything to beat their peers in order to show that they are superior, whether they pay for the app or not.

Over the past few years, there have been many articles about children who are spending thousands of dollars on in-app microtransactions from games such as Clash of Clans, Candy Crush, Game of War, and others. A child in Belgium who was given a credit card by their mother to buy e-books racked up a total $50,000 worth of microtransaction charges in the game Clash of Clans. Another child in England spent $5,900 on the iPad game, Jurassic World, after memorizing their parent’s password for the App Store. While it can be argued that it is not the microtransactions’ fault for these incidents, it is obvious that microtransactions are enabling this behavior.

Microtransactions have their place in the app market when implemented with consumers in mind. Developers can use them as a tool to allow more freedom in the app-creation process. This way, developers will not be deterred from taking the time to make an app that will not be profitable. Making apps is not a simple process, and the reward of economic benefit helps developers feel better about putting forth the effort to make great apps. Microtransactions just need to be done right, and not with the intent to exploit the consumer. Consumers want to feel good about giving an app a chance and not feel like they are just going to be wasting their time by downloading a free app they can’t use. The amount that consumers can invest in apps should be throttled or deferred into donations for the developers to continue making worthwhile apps.

5 Ways an App can Increase Employee Productivity in Manufacturing

5 Ways an App can Increase Employee Productivity in Manufacturing

While we spend most of our efforts helping clients, there are times where we step back and reflect on the lessons we learn through these endeavors. I spent half of 2017 working with Crown, a leading innovator in world-class forklift and material-handling equipment. Through the course of this time, I personally saw changes confirming the app we were developing truly was a key factor in an increase in their employee productivity.

Through app usage, Crown developed a productivity mindset and removed organizational obstacles to their workforce productivity. The app gives employees the ability to work efficiently, keep their equipment operational, and ensure that tools or parts are readily available. Employees are now more productive because the former structures and processes, that consumed valuable time and prevented them from getting things done, have been replaced. Now, with higher labor throughput and with the same amount of relative work, they are more productive.

With these efforts in mind, I compiled the following five ways an app can increase a manufacturer’s employee productivity.

1. Reduce movement to optimize task efficiency
There are many factors that can contribute to unnecessary, time-consuming movement including ineffective floor layout; temporarily displacing material, information, tools, or people; and inefficient working methods. Movement can be reduced by strategically placing objects and information within an app, giving employees quick and easy access to this information. This can eliminate the need for time-consuming searches and demonstrations. For example, video of how to operate equipment can help employees better familiarize themselves with key information about the operations, which will empower them to make informed decisions that help improve their overall productivity.

2. Improve scheduling and plan for interruptions to reduce bottlenecksblog image - schedule effectively and plan for interruptions
Companies must act quickly when something goes wrong, or when their process must be put on hold momentarily because of a malfunct
ion, rejections, or any other changes that may occur. By having access to real-time information regarding employees, tools, and materials, adjustments and accommodations can be made for interruptions. Establishing the right system enables a company to determine the feasibility of scheduling requests, estimate the impact, and even minimize the impact it could have on production. 

3. Improve equipment reliability
Neglecting to maintain equipment, tools, or software puts the process at risk from unaccounted-for downtime. Furthermore, equipment that is poorly maintained or outdated will affect product quality. By taking a more strategic approach and analyzing performance data for key trends, potential issues can be anticipated and maintenance schedules created to extend the longevity of tools, equipment, and software. An app that displays these maintenance schedules gives employees quick access and keeps them informed on equipment status, enabling them to know which equipment needs repairs and which parts are needed for the equipment beforehand. As a result, there will be plans in place to help avoid disruptions to production due to unplanned downtime.

4. Optimize inventory levels to reduce shortages
It’s difficult to be productive when the proper tools to handle a task are unavailable. Companies need to account for and address short count, unexpected delays, and/or late deliveries. An app with this useful information allows accurate and timely visibility of inventory, keeps users informed on what’s running low, possible issues that might arise, and helps address these issues before they become problems that will affect production. In the cases where the shortages are unavoidable, having this system in place will enable users to account for them and even re-assign resources in the meantime.

5. Automate the processblog image - increase and optimize your inventory
The advancements in robotics, artificial intelligence, and machine learning has reached, or in some cases 
surpassed, humans in several different work activities. Having an automated process in production, or even part of an existing process can greatly improve the efficiency and productivity of the process. When the gathering and sending of information is automated, the possibility of human error is eliminated, which effectively prevents disruption to workforce productivity.

At Shockoe, we have been helping businesses increase their productivity by implementing these ideas. We even improved our own process by having systems in place to make our process more productive and efficient, so we can deliver an exceptional product to our clients. Our work with Crown has given us insight on how an application can improve a manufacturer’s productivity. By providing functionality like time tracking, inventory and equipment management, parts logs, order checklists, and more, we have successfully improved productivity for Crown’s workforce. Contact us to take the next step toward improving the quality of your company’s processes and productivity today.

From Creative to Tech: 5 Lessons for Mobile Development Project Management

From Creative to Tech: 5 Lessons for Mobile Development Project Management

shockoe-project-manager-Rebecca-mobile-development-project-managementMy journey into Mobile Development Project Management was almost accidental. I started my career in television production, first as a producer on a reality TV show and then jumping into production at a large advertising agency, helping to create television, radio, and video projects for national brands. But after six years of production, I started gravitating more towards the internal management of teams rather than organizing shoots and productions. I decided to give project management a try, and from the minute I felt the warmth in my heart of seeing my client’s multi-media campaign scheduled out across all deliverables, I knew I was home.

When I made the jump to a tech firm six months ago, I discovered several stigmas placed on project managers at creative agencies:

  • They don’t know agile, having worked in a decades-old process that is viewed as slow, clunky, and requiring several layers of approval.
  • They’re only used to working on large, expensive projects, and are unable to follow a tight budget.
  • They’re “snobs” if the work can’t win a snazzy industry award that looks good on a shelf, they’re not interested.
  • They don’t know digital or tech, and they can only work on traditional media (TV, radio, print).

But while there’s some truth and mostly fiction in all of these stigmas, I believe that my experience at that large, clunky agency has given me important lessons and ideas that I incorporate into my mobile development project management on a daily basis. And as more advertising agencies move into 2018 and beyond, agile is becoming more than just a buzzword; consultancies must incorporate more SDLC (Software Development Life Cycle) mobile project management techniques in order to stay competitive and meet their clients’ needs.

With that, here are five lessons I learned that can be helpful to project managers and team leaders in advertising/marketing and tech:

1. Process should help the work get out faster, and evolve and improve it over time
Agile has become something of a buzzword in advertising, and for good reason. Clients are getting frustrated with the time and cost it takes to get work done. But consultancy creatives have several fears about the agile process: that you can’t quantify the time it takes to get the “big idea,” that clients won’t be able to see work in progress throughout and envision the final product, that daily stand-ups would become too much of a time-suck, and that traditional teams should be structured as a copywriter and art director. A large hurdle for an advertising consultancy to get over is to view the work as an evolving piece, and not a finished product. Sometimes that means releasing something to the client or the public if it’s not finessed to the nth degree, or if it has minor bugs. If you’re constantly updating, engaging, and storytelling, then the focus is more on the brand’s journey over time, and less on one 30-second TV spot. Consultancy teams would also benefit from the structure and accountability that a daily stand-up can provide. Responsibilities are made clear, each employee is accountable for the progress and completion of their own work, and the small team is united in their singular mission of getting the work done. And while Project Managers in both industries keep a full list of functionality or deliverables, tech PMs have more of a voice around Sprint Planning, and work with their clients and team members to determine priorities around features, and keep a fluid backlog of “nice to haves” depending on time and budget.

2. Design should improve the experience, not just impress other industry folks
Software Development Life Cycle Mobile project managementAwards are a necessary evil for any consultancy. They’re motivating for employees and serve as PR and sales tools, attracting new clients and making them aware of the consultancy’s work. But one criticism of a creative consultancy is that work is often done for the sole purpose of winning an award, and not serving the consumer. Yet tech companies may often lean in the opposite direction, where design is sacrificed at the expense of functionality and performance. There is a lesson to be learned from both. There is always a place for impeccable design, but its end goal should be to improve the user experience and solidify the consumer’s impression of the brand. As a project manager, that means involving UX/UI designers and developers throughout the lifespan of a project. My most successful projects have started in a room where a designer and developer are both throwing ideas up on the board, and continue collaborating on functionality, navigation, and UX throughout the process, even in QA. But that’s not meant to undercut the importance of a developer because all the smoke and mirrors in the world can’t hide something that doesn’t actually work. This is why in the agile process, we’re not presenting a PDF to the client, we’re presenting a functioning piece of technology. The code isn’t just the “back end” it’s as much of a client-facing deliverable as a design presentation and needs to be as clean, thorough, and documented as the slickest consultancy deck.

3. Strategic Planning can set a foundation for development too
The best advertising campaign is built upon a solid strategic foundation, and a mobile app or tech project should be no different.Functionality shouldn’t be added just because it’s a hot trend– it should make sense for the overall brand and their consumer, and deliver on a business problem the same way a piece of advertising would. One takeaway that a tech company can glean from a creative consultancy is the importance of a creative brief that’s rooted in the overall brand strategy. If the design is always driven by strategy in addition to the normal technical requirements, your projects will never feel like just a string of new functionality with no big picture in sight– which is frustrating for UX/UI designers and developers alike. While sometimes our clients in IT aren’t privy to the marketing plans and decisions of their brands, it’s our jobs to help them create a strategic plan and roadmap that bridges that relationship and creates consistency across all platforms.

4. Saying “Yes” doesn’t mean “Yes, right this minute”
Mobile Development AgencyIn a company meeting recently, our COO Alex was answering questions about timesheets, and stated, “Your nights and weekends should be your own.” I was immediately shocked and felt like applauding (ok maybe I did a little bit). That a statement like that would be shocking speaks to the culture of creative consultancies– you’re expected to be “on call” at all times, and you almost wear your late night and weekend work like a badge of honor. But why? I admit I’m still a bit stumped on this one. Could it be that creatives maintain that conception is not a science, and they can’t predict when lightning will strike? Or that good ideas don’t come until 3 a.m.? Or that marketing clients operate on faster timelines, with emergencies and last-minute media placements popping up quickly? Either way, I have seen some differences after working at a small tech company. UX/UI designers, developers, and project managers all employ “heads-down” tactics that help them to make better use of their time during the day. Also, daily stand-ups and using tools like JIRA and Slack help teams keep tasks prioritized and get work done quickly.

5. But saying “Yes” isn’t a dirty word either
Mobile Development Project Management Creative and Tech One frustration I hear about project managers in IT and tech is that whenever a new idea is raised, the first answer is “No, it’s not in scope.” But if there’s one thing I’ve learned from being an consultancy producer and project manager, it’s flexibility. Saying “yes” is now innate for me, but how do I make sure that we’re protected as a company and not giving away work for free? It’s still a tricky line to walk, but by ensuring my estimates have room for any bugs or issues that naturally occur in development, I can give us and our clients enough space to get it right, not just done. At that point, a new ask from my client begins a conversation: Is this the right piece of functionality for this release, and will the timing work? Will it make this release that much better, that it is worth the extra hustle? With those questions answered, now we can address the budget: How are we doing overall on our hours? Do we have room to add in extra work, or would this addition cause us to go over? By treating a new ask from a client as a conversation and opportunity instead of a disruption, we can reach the goal that’s shared by creative and tech project managers alike: to create work that we all can be proud of.

 

Note from Editor: 

Our team is all about sharing our “lessons learned” and techniques, here are a couple of other blogs that we think you may find interesting:

Ensure Success with the Right Mobile App Delivery

4 Tips in Designing a Retail Inventory Management App

5 Ways Shockoe Supercharged Mobile Workflow

3 Tips to Start Using Motion in Design

Page 1 of 3123